home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / DNet / DGopher.cpp < prev    next >
Text File  |  1996-07-05  |  69KB  |  2,797 lines

  1. // DGopher.cp 
  2. // by d.g. gilbert, Mar 1992 
  3. // version for DClap, Jan 94
  4. /*
  5.             This code is Copyright (C) 1992 by D.G. Gilbert.
  6.             All Rights Reserved.
  7.  
  8.             You may use this code for your personal use, to provide a non-profit
  9.             service to others, or to use as a test platform for a commercial
  10.             implementation. 
  11.  
  12.             You may not use this code in a commercial product, nor to provide a
  13.             commercial service, nor may you sell this code without express
  14.             written permission of the author.  
  15.             
  16.             gilbertd@bio.indiana.edu 
  17.             Biology Dept., Indiana University, Bloomington, IN 47405 
  18. */
  19.  
  20.  
  21. #include <ncbi.h>
  22. #include <dgg.h> 
  23.  
  24. #include "DTCP.h"
  25. #include "DURL.h"
  26. #include "DGopher.h"
  27. #include "DGoList.h"
  28. #include "DGoPlus.h"
  29. #include "DGoInit.h"
  30. #include "DGoClasses.h"
  31.  
  32. #include <DFile.h>
  33. #include <DList.h>
  34. #include <DIconLib.h>
  35. #include <DView.h>
  36. #include <DChildApp.h>
  37.  
  38.  
  39. enum  commonTCPports {
  40.     kUnknownPort = 0,
  41.     kUnsupportedPort = 0,
  42.     kFileport = 21,
  43.     kFTPport = 21,
  44.     kSMTPport = 25,
  45.     kTelnetport = 23,
  46.     kTN3270port = 23,
  47.     kWhoisport= 43,
  48.     kGopherport = 70,
  49.     kFingerport = 79,
  50.     kHTTPport = 80,
  51.     kPOPport    = 110,
  52.     kNNTPport = 119,
  53.     kWAISport    = 210
  54.     };
  55.  
  56.         //public variables
  57. //Global const    short kGopherPort    = 70;          // default/prime port #
  58.  
  59. Global const    long     kReadMaxbuf  = 1024;
  60.  
  61. Global DIconList*    gGopherIcons = NULL;
  62. Global short        gGopherIconID;
  63. Global short        gIconSize;
  64.  
  65. Global Boolean    gUseDigFolder;
  66. Global short        gDigVol;
  67. Global long            gDigDirID;
  68.  
  69. Global Boolean    gShortFolder = false;
  70. Global Boolean    gListUnknowns = false;
  71. Global Boolean    gDoSuffix2MacMap = false; // !?? started bombing 26Dec94 w/ this != false
  72.  
  73.  
  74. #if !defined(OS_MAC) && !defined(OS_UNIX) && !defined(OS_DOS) && !defined(OS_VMS)
  75. Global char   *    gLocalGopherRoot = "";
  76. Global char   *    gAskWaisPath = "";
  77. #endif
  78. #ifdef OS_MAC
  79. Global char   *    gLocalGopherRoot = ":";
  80. Global char   *    gAskWaisPath = ":bin:askwais";
  81. #endif
  82. #ifdef OS_UNIX
  83. Global char   *    gLocalGopherRoot = ".";
  84. Global char   *    gAskWaisPath = "/bin/askwais";
  85. #endif
  86. #if defined(OS_DOS) || defined (OS_NT)
  87. Global char   *    gLocalGopherRoot = ".";
  88. Global char   *    gAskWaisPath = "\bin\askwais";
  89. #endif
  90. #ifdef OS_VMS
  91. // damn, what is vms syntax for local subdir ??
  92. Global char   *    gLocalGopherRoot = "[Gopher-root]";
  93. Global char   *    gAskWaisPath = "[bin]askwais.exe";
  94. #endif
  95.  
  96.  
  97.         //private    variables
  98.  
  99. //Local DGopher    *gCurrentGopher;
  100.  
  101. Local const char*    gEmptyString    = "";
  102. Local const char* kDefaultLink = "1Title of gopher link\t\tHost.Name.Goes.Here\t70";    
  103. Local const char* kLocalhost = "localhost";
  104.  
  105. Local const    long     kReadToClose = DTCP::kTCPStopAtclose;
  106. Local const    long     kReadToDotCRLF = DTCP::kTCPStopAtdotcrlf;
  107.  
  108. Local const    long     kReadMaxbuf1 = 1 * 1024; 
  109. Local const    long     kMaxInt = 32000;
  110.  
  111. Local const char *gMonths[13] = 
  112.     {"Jan","Feb","Mar","Apr","May","Jun",  "Jul","Aug","Sep","Oct","Nov","Dec","???"};
  113.  
  114.  
  115.  
  116. // DGopherTalk ------------------------------
  117.  
  118.  
  119. DGopherTalk::DGopherTalk(): DTCP()
  120. {
  121.     fLinesRead= 0;
  122.     NullTerm(false);
  123.     //InstallMessageLine( aStatusLine); 
  124. }
  125.  
  126.  
  127.  
  128. void DGopherTalk::OpenQuery( char* host, short port, char* path, char* query, 
  129.                                                         char* viewchoice, char* plus, DFile* plusFile)
  130. {
  131.     void *crlfdotcrlf = (void*) CRLF"."CRLF; //"\015\012";  
  132.     
  133.     ShowMessage("TCP opening connection to host");
  134.     Open((char*)host, port);
  135.     if (Failed()) return;
  136.     if (!WaitedForOpen(0)) return;
  137.     if (path) Send( path, kDontSendNow); 
  138.     if (query) Send( query, kDontSendNow); 
  139.     if (viewchoice) Send( viewchoice, kDontSendNow); 
  140.     
  141.     if (!plus) {
  142.         SendBytes(crlfdotcrlf, 5, kSendNow);    // send dot-cr-lf 
  143.         }
  144.         
  145.     else {
  146.         // if (plus) send plus data header, form depends on plusFile:
  147.         //char *header0 = "\t+\t1\n\r+NBYTES\n\r";
  148.         //char *header1 = "\t+\t1\n\r+-1\n\r";
  149.         //char *header2 = "\t+\t1\n\r+-2\n\r";
  150.     
  151.         if (!plusFile) {
  152.             if (StrChr(plus, kCR)) { // more than one line in plus == ASK block
  153.                 char    header[80];
  154.                 //long len = strlen(plus);
  155.                 //sprintf( header, "\t+\t1\n\r+%d\n\r", len);  // exactly <len> bytes of data
  156.                     // ^^ this isn't understood by server ?!?
  157. #if 1
  158.     // 1apr94 fix for bad DGoFolder selectors  
  159.     //    bad:  path|+view|+|1<cr>ask data
  160.     //     good:  path|+view|1<cr>data or  path|$|1<cr>data  or  path|+|1<cr>data
  161.                 char* selector= (viewchoice) ? "" : "\t$"; // or "+";
  162.                 sprintf( header, "%s\t1"CRLF"+-1"CRLF, selector); // data until dot-cr-lf
  163. #else
  164.                 sprintf( header, "\t+\t1"CRLF"+-1"CRLF); // data until dot-cr-lf
  165. #endif
  166.                 Send( header, kDontSendNow);
  167.                 Send( plus, kDontSendNow);
  168.                 SendBytes(crlfdotcrlf, 5, kSendNow); 
  169.                 }
  170.             else {
  171.                 Send( plus, kDontSendNow);
  172.                 SendBytes(crlfdotcrlf, 5, kSendNow);     
  173.                 }
  174.             }
  175.             
  176.         else {  //if (plusFile)  
  177.             const short     kMaxBuf = 2048;
  178.             char    buffer[kMaxBuf];
  179.             short    err;
  180.             Boolean fullbuf, done= false;
  181.             long fileType = cTEXT;
  182.             ulong    count, lenFile, fat = 0;
  183.             
  184.             err = plusFile->OpenFile();
  185.             err |= plusFile->GetDataLength( lenFile);
  186.             err |= plusFile->GetFileType( fileType);
  187.  
  188.             if (fileType == cTEXT)  {
  189.                 /// ARrrrgggghhhh -- need to do newline translation here (at least if type==TEXT)
  190.                 char    header[80];
  191.                 sprintf( header, "\t+\t1"CRLF"+-1"CRLF); // data until dot-cr-lf
  192.                 Send( header, kDontSendNow);
  193.                 Send( plus, kDontSendNow);
  194.                 SendCRLF(false, kDontSendNow);        // ensure newline  
  195.                 while (! (done || err)) {
  196.                     count= kMaxBuf;
  197.                     err |= plusFile->ReadUntil( buffer, count, *LineEnd);
  198.                     fullbuf = (count+2 > kMaxBuf);
  199.                     if (buffer[count-1] == kCR) {
  200.                         if (fullbuf) count--;
  201.                         else buffer[count++]= kLF; // add lf;
  202.                         }
  203.                     else {
  204.                         if (!fullbuf) {
  205.                             buffer[count++]= kCR; 
  206.                             buffer[count++]= kLF; 
  207.                             } 
  208.                         }
  209.                     if (count==3 && 0==memcmp(buffer, "."CRLF, 3))  
  210.                         this->SendBytes(".."CRLF, 4, kDontSendNow); // make sure we don't send terminator by mistake
  211.                     else
  212.                         this->SendBytes(buffer, count, kSendNow); //kDontSendNow
  213.                     if (fullbuf) this->SendCRLF(kDontSendNow);
  214.                     
  215.                     err |= plusFile->GetDataMark(fat);
  216.                     done|= fat >= lenFile;
  217.                     }
  218.                 SendCRLF(false, kDontSendNow);        // ensure newline  
  219.                 SendBytes("."CRLF, 3, kSendNow);    // send dot-cr-lf 
  220.                 }
  221.             
  222.             else {
  223.                 char    header[80];
  224.                 long len = strlen(plus) + lenFile;
  225.                 //sprintf( header, "\t+\t1"CRLF"+%d"CRLF, len);  // exactly <len> bytes of data
  226.                         // ^^ this isn't understood by server ?!?
  227.                 sprintf( header, "\t+\t1"CRLF"+-2"CRLF); // data until close
  228.                 Send( header, kDontSendNow);
  229.                 Send( plus, kDontSendNow);
  230.                 //SendCRLF(false, kDontSendNow);        // ??! ensure newline  -- not w/ fixed bytecount...
  231.                 while (! (done || err)) {
  232.                     count= kMaxBuf;
  233.                     err |= plusFile->ReadData( buffer, count);
  234.                     this->SendBytes(buffer, count, kSendNow);
  235.                     err |= plusFile->GetDataMark(fat);
  236.                     done|= fat >= lenFile;
  237.                     }
  238.                 }
  239.                 
  240.              err= plusFile->CloseFile();
  241.             }
  242.         }
  243.         
  244. //  long ticktock= TickCount() + fTimeout;
  245. //    while (!CharsAvailable() && TickCount() < ticktock && !UserBreak() )
  246. //            StatusMessage();            
  247.     SetShowProgress( true);
  248.  
  249. }
  250.  
  251.  
  252.  
  253.  
  254. Boolean DGopherTalk::ReadALine( CharPtr appendToHandle, CharPtr& result)
  255. {    
  256.     result= NULL;
  257.     fResultNew= 0;
  258.     CharPtr data= RecvLine(); //RecvUpTo( true, kLF, NULL);  // includes lf+null at end
  259.     //- fEndofMessage= false; << !?! RecvUpTo NO LONGER sets this true at Linefeed ?
  260.     Boolean readone= (data!=NULL);
  261.     fLinesRead++;
  262.     if (readone) {
  263.         if ( data[0] == '.' &&  data[1] == kCR) fEndofMessage= true; 
  264.                 //  ^^ this changes w/ subclasses...
  265.  
  266.                 // some damn terminator patching -- always end string w/ Return, no lf or null 
  267.         long    len= StrLen(CharPtr(data));
  268.         //len--; //drop null, always there from RecvUpTo() << NO, 
  269.         //if (len < kMaxInt) << do we need to stop at 32K ??
  270.         {
  271.             if ( data[len-1] == kLF) len--;
  272.             if ( data[len-1] != kCR) data[len++]= *LineEnd; 
  273.             }
  274.         data= (char*) MemMore( data, Max(0,len));
  275.         
  276.         if (appendToHandle == NULL)
  277.             appendToHandle= data;
  278.         else {
  279.             long oldlen= StrLen(appendToHandle);
  280.             appendToHandle= (char*)MemMore( appendToHandle, oldlen+len+1);
  281.             MemCopy( appendToHandle+oldlen, data, len);
  282.             appendToHandle[oldlen+len]= 0;
  283.             }
  284.         }
  285.     result= appendToHandle;
  286.     return readone;
  287. }
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294. // DocLink ------------------------------
  295.  
  296.  
  297. DocLink::DocLink() : 
  298.     fType(kNoLink), fParag(0), fChar(0)
  299. {
  300.     string.fString= NULL;
  301. }
  302.  
  303. DocLink::~DocLink()  
  304. {
  305.     if (fType == kString) MemFree( string.fString);
  306. }
  307.  
  308. void DocLink::Clear() 
  309.     if (fType == kString) MemFree( string.fString);
  310.     fType= kNoLink; 
  311. }
  312.         
  313. void DocLink::DupData()
  314. {
  315.     if (fType == kString) 
  316.         string.fString= StrDup( string.fString);
  317. }
  318.         
  319. char * DocLink::GetString( short& skipval) 
  320.     skipval= string.fSkip; 
  321.     return string.fString; 
  322. }
  323.  
  324. void DocLink::SetString( const char* thestr, short skipvalue)
  325. {
  326.     Clear();
  327.     fType= kString;
  328.     string.fString= StrDup(thestr);
  329.     string.fSkip= skipvalue;
  330. }
  331.  
  332. void DocLink::SetSkip( short skipvalue) 
  333.     if (fType == kString) string.fSkip= skipvalue; 
  334. }
  335.  
  336. void DocLink::SetRect( Nlm_RecT therect)
  337. {
  338.     Clear();
  339.     fType= kRect;
  340.     rect.fRect= therect;
  341. }
  342.  
  343. void DocLink::GetLine( short& atparag, short& atchar, short& endparag, short& endchar )
  344. {
  345.     atparag= fParag;
  346.     atchar= fChar;
  347.     endparag= line.fEndParag;
  348.     endchar= line.fEndChar;
  349. }
  350.     
  351. void DocLink::SetLine( short atparag, short atchar, short endparag, short endchar )
  352. {
  353.     Clear();
  354.     fType= kLine;
  355.     fParag= atparag;
  356.     fChar= atchar;
  357.     line.fEndParag= endparag;
  358.     line.fEndChar= endchar;
  359. }
  360.  
  361. void DocLink::GetLineRect( Nlm_RecT& therect, short& atparag, short& atchar )
  362. {
  363.     therect= rect.fRect;
  364.     atparag= fParag;
  365.     atchar= fChar;
  366. }
  367.     
  368. void DocLink::SetLineRect( Nlm_RecT therect, short atparag, short atchar)
  369. {
  370.     Clear();
  371.     fType= kLineRect;
  372.     fParag= atparag;
  373.     fChar= atchar;
  374.     rect.fRect= therect;
  375. }
  376.  
  377.  
  378.  
  379.  
  380.  
  381. // DGopherAbstract ------------------------------
  382.  
  383. class DGopherAbstract : public DObject
  384. {
  385. public:
  386.     short fLines;
  387.     long    fSize;
  388.     char* fText;
  389.     
  390.     DGopherAbstract( char* text);
  391.     ~DGopherAbstract() { Clear(); }
  392.     
  393.     virtual DObject* Clone();
  394.     void Set( const char* text);
  395.     void Clear();
  396.     void Install( const char* text) { Clear(); Set( text); }
  397.     void Draw( Nlm_RecT& area);
  398.     short Height();
  399. };
  400.  
  401. DGopherAbstract::DGopherAbstract( char* text) : 
  402.         fLines(0), fSize(0), fText(NULL) 
  403.     Set( text); 
  404. }
  405.  
  406. void DGopherAbstract::Clear()
  407. {
  408.     MemFree( fText);
  409.     fSize= fLines= 0;
  410. }
  411.  
  412. DObject* DGopherAbstract::Clone()
  413. {
  414.     DGopherAbstract* myClone= (DGopherAbstract*) DObject::Clone();
  415.     myClone->fText= StrDup(fText);
  416.     return myClone;
  417. }
  418.  
  419. void DGopherAbstract::Set( const char* text)
  420. {
  421.     char *cp, *dp, c, lastc;
  422.     Boolean havevis= false;
  423.     long bytes= StringLen( text);
  424.     if (bytes>0) {
  425.         fText= (char*) MemNew( bytes+1);
  426.         fLines= 1;
  427.         for (cp= (char*)text, dp= fText, lastc= 0; *cp != 0; cp++) {
  428.             c= *cp;
  429.             if (c == kCR) { 
  430.                 if (havevis) { *dp++= NEWLINE;  fLines++; }
  431.                 }
  432.             else if (c == kLF) {
  433.                 if (lastc == kCR) ; // eat c
  434.                 else if (havevis) { *dp++= NEWLINE;  fLines++; }
  435.               }
  436.             else if (havevis) *dp++= c; 
  437.             else { havevis= isgraph(c); if (havevis) *dp++= c; }
  438.             lastc= c;
  439.             }
  440.         while ( fLines && dp > fText && dp[-1] == NEWLINE) { 
  441.              // drop trailing blank lines
  442.             fLines--; dp--; 
  443.             }    
  444.       *dp= 0;
  445.       fSize= dp - fText;
  446.       }
  447. }
  448.  
  449. void DGopherAbstract::Draw( Nlm_RecT& area)
  450. {
  451.     if (fText) {
  452.         //? Nlm_DrawString(&area, fText, 'l', false);
  453.         Dgg_DrawTextbox( &area, fText, fSize, 'l', false);
  454.         }
  455. }
  456.  
  457. short DGopherAbstract::Height()
  458. {
  459.     if (fLines) 
  460.         return (fLines * Nlm_LineHeight());
  461.     else 
  462.         return 0;
  463. }
  464.  
  465.  
  466.  
  467.  
  468.  
  469.  
  470.  
  471.  
  472.  
  473. // DGopher ------------------------------
  474.  
  475.  
  476. DGopher::DGopher( char ctype, const char* link) :
  477.     DTaskMaster( 0, NULL, NULL)
  478. {
  479.     Initialize();
  480.     SetLink( ctype, link);
  481. }
  482.  
  483. DGopher::DGopher() :
  484.     DTaskMaster( 0, NULL, NULL)
  485. {
  486.     Initialize();
  487.     SetLink( *kDefaultLink, kDefaultLink);
  488. }
  489.  
  490. DGopher::DGopher( DGopher* aGopher)  :
  491.     DTaskMaster( 0, NULL, NULL) 
  492. {
  493.     Initialize();
  494.     CopyGopher( aGopher);
  495. }
  496.  
  497.  
  498. DGopher::~DGopher()  
  499. {
  500.     DeleteLink(false);
  501.     fInfo= (char*) MemFree( fInfo);
  502. }
  503.  
  504. Boolean DGopher::suicide(void) 
  505.     if (GetOwnerCount() <= 1) { 
  506.         delete this; 
  507.         return true; 
  508.         }
  509.     else 
  510.         return DObject::suicide();
  511. }
  512.  
  513. Boolean DGopher::suicide(short ownercount)
  514. {
  515.     if (ownercount < 1) {
  516.         delete this; 
  517.         return true; 
  518.         }
  519.     else 
  520.         return DObject::suicide(ownercount);
  521. }
  522.  
  523. DObject* DGopher::Clone()
  524. {
  525.     //DGopher* aGopher= (DGopher*)DObject::Clone();
  526.     DGopher* aGopher= new DGopher();
  527.     aGopher->CopyGopher( this);
  528.     return aGopher;
  529. }
  530.  
  531.  
  532.  
  533. void DGopher::Initialize()
  534. {
  535.     fLink= NULL;
  536.     fLinkLen= 0;
  537.     fItitle= 0;
  538.     fIdate = 0;     
  539.     fIsize = 0;     
  540.     fIpath = 0;     
  541.     fIhost = 0;     
  542.     fIport = 0;     
  543.     fIplus = 0; 
  544.  
  545.     fType    = kTypeError; //== ancestor doesn't know much...
  546.     fProtocol = kGopherprot;
  547.     fIsLocal= false;
  548.     fPort    = kGopherport;
  549.     fURL= NULL;
  550.     fDate= NULL; fDateLong= 0;
  551.     fDataSize= NULL; fSizeLong= 0;
  552.     fInfo= NULL;
  553.     fInfoSize= 0;
  554.     fInfoHandler= NULL;
  555.     
  556.     fOwnerList= NULL;
  557.     fStatusLine= NULL;
  558.  
  559.     fDeleted= false;
  560.     fInUseCount= 1;
  561.     fTalker= NULL;
  562.     fMacType= cTEXT;
  563.     fMacSire= cEDIT;
  564.     fTransferType= kTransferText; //text/lines or binary or none ?
  565.     fSaveToDisk= false;
  566.     fLaunch= false;
  567.     fQuery= NULL;
  568.     fQueryGiven= NULL;
  569. #if 1
  570.     fDocLink= NULL;    //ClearDocLink();
  571. #else
  572.     fMenuString= NULL;
  573.     fMenuStringSkip= 0;
  574.     fDocLinkParag= 0;
  575.     fDocLinkChar= 0;
  576.     fDocLinkLen= 0;
  577. #endif
  578.  
  579.     fCommand= cNoCommand;
  580.     fThreadState= kThreadNotStarted;
  581.     fBytesExpected= 0;
  582.     fBytesReceived= 0;
  583.     fConnectTime= 0;    
  584.     fAppFile= NULL;
  585.     
  586.         // gopher+
  587.     fShortFolder= gShortFolder; //false;
  588.     fHasAsk= false;
  589.     fIsMap= false;
  590.     fHaveReply= false;
  591.     fReplyFile= NULL;
  592.     fReplyData= NULL;
  593.     fIsPlus= kGopherPlusDontKnow;
  594.     fQueryPlus= NULL;
  595.     fAdminEmail= NULL;
  596.     fViews= NULL;                    // list of CGopherItemView
  597.     fViewchoice= 0;
  598.     fAskers    = NULL;
  599.     fAbstract= NULL;
  600.     fGoMenuBlock= NULL;
  601.     fInfo    = (char*)Nlm_MemGet(1, true); 
  602.     fInfoSize= 0; // fInfoSize is size of data not including terminating NULL !@!@$#@# 
  603. }
  604.  
  605. void DGopher::CopyGopher( DGopher* aGopher)  
  606. {
  607.     if (aGopher) {
  608.     // This memcpy was bad...problem was NULL aGopher !!!
  609.     //memcpy(&fType, &aGopher->fType, sizeof(DGopher)); //<< bad for subclass w/ larger size !
  610.  
  611.     fType     = aGopher->fType;                  
  612.     fItitle    = aGopher->fItitle; 
  613.     fIdate    = aGopher->fIdate;         
  614.     fIsize    = aGopher->fIsize; 
  615.     fIpath    = aGopher->fIpath; 
  616.     fIhost    = aGopher->fIhost;     
  617.     fIport    = aGopher->fIport;     
  618.     fIplus    = aGopher->fIplus;     
  619.     fPort        = aGopher->fPort;      
  620.     fLink        = (char*) MemDup( aGopher->fLink, aGopher->fLinkLen);
  621.     fLinkLen= aGopher->fLinkLen;
  622.  
  623.     fDate        = (char*) StrDup( (CharPtr)aGopher->fDate);
  624.     fDateLong    = aGopher->fDateLong;     
  625.     fDataSize    = (char*) StrDup( (CharPtr)aGopher->fDataSize);
  626.     fSizeLong = aGopher->fSizeLong;     
  627.  
  628.     fAdminEmail= (char*) StrDup( (CharPtr)aGopher->fAdminEmail);    
  629.     fViews    = aGopher->CloneViews();
  630.     fAskers    = aGopher->CloneAskers();
  631.     if (aGopher->fAbstract) fAbstract = (DGopherAbstract*) aGopher->Clone(); 
  632.     fIsPlus    = aGopher->fIsPlus;         
  633.     fShortFolder    = aGopher->fShortFolder; 
  634.     fHasAsk    = aGopher->fHasAsk;         
  635.     fHaveReply = aGopher->fHaveReply;     
  636.     if (aGopher->fReplyFile) fReplyFile = (DFile*) aGopher->fReplyFile->Clone();     
  637.     fReplyData = StrDup( aGopher->fReplyData);         
  638.     if (aGopher->fGoMenuBlock) fGoMenuBlock= new DGoMenuBlock(this, aGopher->fGoMenuBlock->fData);
  639.     else fGoMenuBlock= NULL;
  640.  
  641.     fProtocol= aGopher->fProtocol;      
  642.     fURL        = (char*) StrDup( (CharPtr)aGopher->fURL);
  643.     fIsLocal= aGopher->fIsLocal;
  644.     fIsMap    = aGopher->fIsMap;         
  645.  
  646.     MemFree( fInfo); 
  647.     fInfoSize= aGopher->fInfoSize;
  648.     fInfo= (char*)MemDup( aGopher->fInfo, fInfoSize+1);
  649.     fInfoHandler= aGopher->fInfoHandler; //??
  650.     
  651.     fQuery = aGopher->fQuery;             
  652.     fQueryPlus = aGopher->fQueryPlus;    
  653.     fQueryGiven = StrDup( aGopher->fQueryGiven);         
  654.     
  655.     //fTransferType    = aGopher->fTransferType;  
  656.     //fDeleted= false;
  657.     //fMacType    = aGopher->fMacType;             // PRESERVE for CopyToNewKind
  658.     //fMacSire    = aGopher->fMacSire;            // PRESERVE for CopyToNewKind
  659.     //fSaveToDisk    = aGopher->fSaveToDisk;    // PRESERVE for CopyToNewKind
  660.     //fLaunch        = aGopher->fLaunch;                // PRESERVE for CopyToNewKind
  661.     //fCommand    = aGopher->fCommand;             // PRESERVE for CopyToNewKind
  662.     //fViewchoice    = aGopher->fViewchoice;    // PRESERVE for CopyToNewKind
  663.     //fOwnerList= NULL; //!? 
  664.     fStatusLine= aGopher->fStatusLine; 
  665.     //fTalker= NULL;        //? 
  666.     fBytesExpected= aGopher->fBytesExpected;
  667.     //fBytesReceived= aGopher->fBytesReceived;
  668.     //fConnectTime= aGopher->fConnectTime;
  669.     //fThreadState= aGopher->fThreadState; 
  670.     //fAppFile = NULL; //??
  671.     //fInUseCount= 1;
  672.     fDocLink= aGopher->fDocLink;
  673.     if (fDocLink) fDocLink->DupData();
  674.     }
  675. }
  676.  
  677. void DGopher::DeleteInfo()
  678. {
  679.     if (fInfo) fInfo= (char*) MemMore( fInfo, 1);
  680.     else fInfo= (char*) MemNew(1);
  681.     *fInfo= 0;
  682.     fInfoSize= 0;
  683. }
  684.  
  685. void DGopher::DeleteViews()  
  686. {
  687.     if (fViews) {
  688.         fViews->FreeAllObjects();
  689.         fViews->suicide();
  690.         fViews= NULL;
  691.         }
  692. }
  693.  
  694. DList* DGopher::CloneViews()  
  695. {
  696.     DList* newViews= NULL;
  697.     if (fViews) {
  698.         newViews= new DList();
  699.         long i, n= fViews->GetSize();
  700.         for (i= 0; i<n; i++) newViews->InsertLast( fViews->At(i)->Clone());
  701.         }
  702.     return newViews;
  703. }
  704.  
  705.  
  706. void DGopher::DeleteAskers()  
  707. {
  708.     if (fAskers) {
  709.         fAskers->FreeAllObjects();
  710.         fAskers->suicide();
  711.         fAskers= NULL;
  712.         }
  713. }
  714.  
  715. DList* DGopher::CloneAskers()  
  716. {
  717.     DList* newAskers= NULL;
  718.     if (fAskers) {
  719.         newAskers= new DList();
  720.         long i, n= fAskers->GetSize();
  721.         for (i= 0; i<n; i++) newAskers->InsertLast( fAskers->At(i)->Clone());
  722.  
  723.         }
  724.     return newAskers;
  725. }
  726.  
  727.  
  728.  
  729. void DGopher::DeleteLink(Boolean onlyInfoline)  
  730. {
  731.     fLink    =  (char*) MemFree( fLink);
  732.     fLinkLen= 0;
  733.         // zero the indices into flink
  734.      fItitle = fIdate= fIsize= fIpath= fIhost= fIport= fIplus = 0;
  735.  
  736.     if (!onlyInfoline) {    
  737.         if (fDate) fDate    = (char*)MemFree( (CharPtr)fDate);    
  738.         if (fDataSize) fDataSize= (char*)MemFree( (CharPtr)fDataSize);
  739.         if (fAppFile) fAppFile= (char*)MemFree( fAppFile);
  740.         if (fAdminEmail) fAdminEmail    = (char*)MemFree( (CharPtr)fAdminEmail);    
  741.         if (fGoMenuBlock) { delete fGoMenuBlock; fGoMenuBlock= NULL; }
  742.         if (fAbstract) { delete fAbstract; fAbstract= NULL;  }
  743.         if (fQueryGiven) fQueryGiven= (char*)MemFree( fQueryGiven);
  744. #if 1
  745.         if (fDocLink) delete fDocLink; fDocLink= NULL;
  746. #else
  747.         if (fMenuString) fMenuString= (char*)MemFree( fMenuString);
  748. #endif
  749.  
  750.         DeleteViews();
  751.         DeleteAskers();
  752.         }
  753. }
  754.  
  755.  
  756.  
  757.  
  758. Boolean DGopher::HasDocLink()
  759. {
  760.     return (fDocLink && fDocLink->HasLink());
  761.     //return (fDocLinkParag || fDocLinkChar || fDocLinkLen);
  762. }
  763.  
  764. #if 0
  765. void DGopher::GetDocLink( short& atParagraph, short& atChar, short& length)
  766. {
  767.     atParagraph= fDocLinkParag;
  768.     atChar= fDocLinkChar;
  769.     length= fDocLinkLen;
  770. }
  771.  
  772. void DGopher::SetDocLink( short atParagraph, short atChar, short length)
  773. {
  774.     fDocLinkParag= atParagraph;
  775.     fDocLinkChar= atChar;
  776.     fDocLinkLen= length;
  777. }
  778.  
  779. void DGopher::ClearDocLink()
  780. {
  781.     fDocLinkParag= 0;
  782.     fDocLinkChar= 0;
  783.     fDocLinkLen= 0;
  784. }
  785. #endif
  786.  
  787.  
  788.  
  789. void DGopher::AddView( const char* viewkind)
  790. {
  791.     if (!fViews) fViews = new DList();
  792.     DGopherItemView* giv= new DGopherItemView( viewkind);
  793.     fViews->InsertLast( giv);
  794. }
  795.  
  796.  
  797. void DGopher::SetPlusInfo( short isPlus, Boolean hasAsk, char* plus)
  798. {
  799.     char *cp, *ep, ec, ecl, *sp, *sep, cep;
  800.     Boolean done= false;
  801.     Boolean isHTMLform= false;
  802.     
  803. #define FindNextPlus(cp)     { \
  804.     for (ep=cp,ecl=*(cp-1),ec=*ep; ec!=0 && !((ec=='.' || ec=='+') && ecl<' '); ) \
  805.         {ecl= ec; ec= *(++ep);} }
  806.     
  807.     fIsPlus= isPlus;
  808.     fHasAsk= hasAsk;
  809.     ClearAskReply();
  810.     if (!plus) return;
  811.  
  812.     cp= plus;
  813.     while (*cp!=0) {
  814.         while (*cp!='+' && *cp!=0) cp++;
  815.         
  816.         if (0==Strncasecmp(cp, "+ADMIN:",7)) {
  817.             cp += 7;
  818.             FindNextPlus(cp);
  819.             while (cp < ep) {
  820.                 while (*cp <= ' ' && *cp!=0 && cp<ep) cp++; // skip whitespace, newlines
  821.                 if (0==Strncasecmp(cp, "Admin:",6)) {
  822.                     // Admin: Don Gilbert  <archive@bio.indiana.edu>
  823.                     MemFree((CharPtr)fAdminEmail);
  824.                     sp = cp + 6;
  825.                     for (sep= sp; *sep >= ' ' && sep<ep; sep++) ; // skip over other words
  826.                     cep= *sep; *sep= 0;
  827.                     fAdminEmail= (char*)StrDup(sp);
  828.                     *sep= cep;
  829.                     }
  830.                     
  831.                 else if (0==Strncasecmp(cp, "Mod-Date:",9)) {
  832.                     sp= StrChr(cp,'<');
  833.                     if (sp) {
  834.                             // Mod-Date: Thu Feb  4 13:46:47 1993 <19930204134647>
  835.                             // convert <19930204134647> to 04Feb93
  836.                         char    sdate[50];
  837.                         sp++;
  838.                         // save complete date for sorts, etc.
  839.                         sep= sp+10; cep= *sep; *sep= 0;
  840.                         fDateLong= atol(sp);
  841.                         *sep= cep;
  842.  
  843.                         sdate[0]= 0;
  844.                         sep= sp+6; //day
  845.                         strncat(sdate, sep, 2);
  846.                         
  847.                         sep= sp+6; cep= *sep; *sep= 0;
  848.                         long i= atol(sp+4);  // month
  849.                         *sep= cep;
  850.                         if (i<1 || i>12) strncat(sdate, gMonths[12], 3);
  851.                         else strncat(sdate, gMonths[i-1], 3);
  852.                         strncat( sdate, sp+2, 2); // year
  853.                         MemFree((CharPtr)fDate);
  854.                         fDate= (char*) StrDup( sdate);
  855.                         }
  856.                     }
  857.                 while (*cp >= ' ' && *cp!=0 && cp<ep) cp++; // skip over other words
  858.                 }
  859.             } // +ADMIN
  860.             
  861.         else if (0==Strncasecmp(cp, "+VIEWS:",7)) {
  862.             cp += 7;
  863.             FindNextPlus(cp);
  864.             DeleteViews();
  865.             fViews = new DList();
  866.             while (cp < ep) {
  867.                 while (*cp <= ' ' && *cp!=0 && cp<ep) cp++; // skip whitespace, newlines
  868.                 if (*cp!=0 && cp<ep) {
  869.                     for (sep= cp; *sep >= ' ' && sep<ep; sep++) ; // skip over other words
  870.                     cep= *sep; *sep= 0;
  871.                     DGopherItemView* giv= new DGopherItemView( cp);
  872.                     fViews->InsertLast( giv);
  873.                     if (fDataSize==NULL) {
  874.                         fSizeLong= atol( (char*) giv->DataSize());
  875.                         char snum[50];
  876.                         if (fSizeLong) sprintf( snum, "%luk", fSizeLong);
  877.                         else strcpy(snum,"<1k");
  878.                         fDataSize= (char*) StrDup( snum);
  879.                         }
  880.                     *sep= cep;
  881.                     cp= sep;
  882.                     }        
  883.                 }
  884.             } // +VIEWS
  885.             
  886.         else if (0==Strncasecmp(cp, "+ASKHTML:",9)) {
  887.             // this format is like +ASK except each item has HTML name tag immediately
  888.             // after ask kind:  " Choose:name:label\tchoice1\tchoice2"
  889.             // " Ask:name2:label2\tstring"
  890.             cp += 9; 
  891.             isHTMLform= true;
  892.             goto HandleAskForm;
  893.             }
  894.             
  895.         else if (0==Strncasecmp(cp, "+ASK:",5)) {
  896.             cp += 5;
  897.             isHTMLform= false;
  898. HandleAskForm:
  899.             FindNextPlus(cp);
  900.             DeleteAskers();
  901.             fAskers = new DList;
  902.             while (cp < ep) {
  903.                 while (*cp <= ' ' && *cp!=0 && cp<ep) cp++; // skip whitespace, newlines
  904.                 if (*cp!=0 && cp<ep) {
  905.                     for (sep= cp; (*sep>=' ' || *sep== '\t') && sep<ep; sep++) ; // skip over other words
  906.                     cep= *sep; *sep= 0;
  907.                     DGopherItemAsk* gia= new DGopherItemAsk( cp, isHTMLform);
  908.                     if (gia && gia->fKind == kGopherAskUnknown) delete gia;
  909.                     else fAskers->InsertLast( gia);
  910.                     *sep= cep;
  911.                     cp= sep;
  912.                     }        
  913.                 }
  914.             }  //+ASK
  915.             
  916.         else if (0==Strncasecmp(cp, "+ABSTRACT:",10)) {
  917.             cp += 10;
  918.             FindNextPlus(cp);
  919.             cep= *ep; *ep= 0;
  920.             if (fAbstract) fAbstract->Install( cp); 
  921.             else fAbstract= new DGopherAbstract( cp); 
  922.             *ep= cep;
  923.             } //+ABSTRACT
  924.  
  925.         else if (0 == Strncasecmp( cp, "+URL:", 5) ) {
  926.             cp += 5;
  927.             FindNextPlus(cp);
  928.             //if (fURL) MemFree( fURL);
  929.             cep= *ep; *ep= 0;
  930. #if 0
  931.             if ( DURL::ParseURL( this, cp, ep-cp) ) ;
  932. #else
  933.             char* saven= StrDup( (char*)this->GetName());
  934.             if ( DURL::ParseURL( this, cp, ep-cp) ) ;
  935.             if (saven && *saven) this->StoreName(saven); 
  936.             MemFree(saven);
  937. #endif
  938.             *ep= cep;
  939.             }
  940.  
  941.         else if (0==Strncasecmp(cp, "+ISMAP",6)) {  // dgg addition
  942.             cp += 6;
  943.             FindNextPlus(cp);
  944.             fIsMap= true;
  945. // having problems w/ other settings !?!?
  946. // ?? need special map type ?
  947.             fIsPlus= kGopherPlusYes;
  948.             fTransferType= kTransferBinary; 
  949.             //fCommand= cNewGopherText; // ???
  950.             } //+ISMAP
  951.             
  952.         else if (0==Strncasecmp(cp, "+MENU:",6)) { // dgg addition
  953.             cp += 6;
  954.             //FindNextPlus(cp);
  955.             Boolean found= false;
  956.             ep= cp; 
  957.             ecl= *(cp-1);
  958.             ec= *ep; 
  959.             while (!found) {
  960.                 ecl= ec; 
  961.                 ++ep;
  962.                 ec= *ep;
  963.                 if (ec == 0) found= true;
  964.                 else if ((ecl == kCR || ecl == kLF) && (ec == '+' || ec == '.')) found= true;
  965.                 }  
  966.  
  967.             if (fGoMenuBlock) delete fGoMenuBlock;
  968.             cep= *ep; *ep= 0;
  969.             fGoMenuBlock= new DGoMenuBlock(this, cp);
  970.             *ep= cep;
  971.             } //+MENU
  972.             
  973.         // +MENUSTRING:
  974.         else if (0==Strncasecmp(cp, "+MENUSTRING:",12)) { // dgg addition
  975.             cp += 12;
  976.             while (*cp && *cp <= ' ') cp++;
  977.             if (*cp == '"') {
  978.                 cp++; ep= cp;
  979.                 while (*ep && *ep != '"') ep++;
  980.                 }
  981.             else if (*cp == '\'') {
  982.                 cp++; ep= cp;
  983.                 while (*ep && *ep != '\'') ep++;
  984.                 }
  985.             else {
  986.                 ep= cp;
  987.                 while (*ep && *ep > ' ') ep++;
  988.                 }
  989.             cep= *ep; *ep= 0;
  990.             //if (fMenuString) MemFree(fMenuString);
  991.             //fMenuString= StrDup(cp);
  992.             if (!fDocLink) fDocLink= new DocLink();
  993.             fDocLink->SetString( cp);
  994.             *ep= cep;
  995.             
  996.                 // look for skip value
  997.             cp= ep; if (*cp) cp++;
  998.             while (*cp && *cp <= ' ') cp++;
  999.             if (isdigit(*cp)) 
  1000.                 fDocLink->SetSkip( atol(cp)); 
  1001.                 //fMenuStringSkip= atol( cp);
  1002.             
  1003.             FindNextPlus(cp);
  1004.             }
  1005.  
  1006.             // +MENULINE:  #parag  #startchar  #endparag  #stopchar
  1007.         else if (0==Strncasecmp(cp, "+MENULINE:",10)) { // dgg addition
  1008.             cp += 10;
  1009.             short  atpar, atchar, endpar, endchar;
  1010. #if 1
  1011.             while (*cp && *cp <= ' ') cp++;
  1012.             atpar= atol( cp);
  1013.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1014.             atchar= atol( cp);
  1015.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1016.             endpar= atol( cp);
  1017.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1018.             endchar= atol( cp);
  1019. #else
  1020.     // bad, skipping 1st number !!
  1021.             sscanf( cp, "%d%d%d%d", &atpar, &atchar, &endpar, &endchar);
  1022. #endif
  1023.             if (!fDocLink) fDocLink= new DocLink();
  1024.             fDocLink->SetLine( atpar, atchar, endpar, endchar);
  1025.             FindNextPlus(cp);
  1026.             }
  1027.  
  1028.             // +MENURECT:  #left  #top  #right  #bottom
  1029.         else if (0==Strncasecmp(cp, "+MENURECT:",10)) { // dgg addition
  1030.             short  left, top, right, bottom;
  1031.             Nlm_RecT    r;
  1032.             cp += 10;
  1033. #if 1
  1034.             while (*cp && *cp <= ' ') cp++;
  1035.             left= atol( cp);
  1036.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1037.             top= atol( cp);
  1038.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1039.             right= atol( cp);
  1040.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1041.             bottom= atol( cp);
  1042. #else
  1043.     // bad, skipping 1st number !!
  1044.             sscanf( cp, "%d %d %d %d ", &left, &top, &right, &bottom);
  1045. #endif
  1046.             Nlm_LoadRect( &r, left, top, right, bottom);
  1047.             if (!fDocLink) fDocLink= new DocLink();
  1048.             fDocLink->SetRect(r);
  1049.             FindNextPlus(cp);
  1050.             }
  1051.  
  1052.             // +MENULINERECT:  #parag #startchar #left  #top  #right  #bottom
  1053.         else if (0==Strncasecmp(cp, "+MENULINERECT:",14)) { // dgg addition
  1054.             short  atpar, atchar, left, top, right, bottom;
  1055.             Nlm_RecT    r;
  1056.             cp += 14;
  1057. #if 1
  1058.             while (*cp && *cp <= ' ') cp++;
  1059.             atpar= atol( cp);
  1060.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1061.             atchar= atol( cp);
  1062.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1063.             left= atol( cp);
  1064.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1065.             top= atol( cp);
  1066.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1067.             right= atol( cp);
  1068.             while (isdigit(*cp)) cp++; while (*cp && *cp <= ' ') cp++;
  1069.             bottom= atol( cp);
  1070. #else
  1071.     // bad, skipping 1st number !!
  1072.             sscanf( cp, "%d %d %d %d %d %d ", 
  1073.                         &atpar, &atchar, &left, &top, &right, &bottom);
  1074. #endif
  1075.             Nlm_LoadRect( &r, left, top, right, bottom);
  1076.             if (!fDocLink) fDocLink= new DocLink();
  1077.             fDocLink->SetLineRect(r, atpar, atchar);
  1078.             FindNextPlus(cp);
  1079.             }
  1080.  
  1081.         else if (0==Strncasecmp(cp, "+QUERYSTRING:",13)) { // dgg addition
  1082.             cp += 13;
  1083.             while (*cp && *cp <= ' ') cp++;
  1084.             if (*cp == '"') {
  1085.                 cp++; ep= cp;
  1086.                 while (*ep && *ep != '"') ep++;
  1087.                 }
  1088.             else if (*cp == '\'') {
  1089.                 cp++; ep= cp;
  1090.                 while (*ep && *ep != '\'') ep++;
  1091.                 }
  1092.             else {
  1093.                 ep= cp;
  1094.                 while (*ep && *ep > ' ') ep++;
  1095.                 }
  1096.             cep= *ep; *ep= 0;
  1097.             if (fQueryGiven) MemFree(fQueryGiven);
  1098.             fQueryGiven= StrDup(cp);
  1099.             *ep= cep;
  1100.             FindNextPlus(cp);
  1101.             }
  1102.             
  1103.         else if (0==Strncasecmp(cp, "+INFO:",6)) {
  1104.             cp += 6;
  1105.             // this probably means we messed up packaging plus data for this call
  1106.             // ?? should we exit/fail ??
  1107.             FindNextPlus(cp);
  1108.             } //+INFO
  1109.                         
  1110.         else {
  1111.             cp++;
  1112.             FindNextPlus(cp);
  1113.             } // +OTHER  +COMMENT
  1114.             
  1115.         cp= ep;    
  1116.         }            
  1117.  
  1118. #undef FindNextPlus    
  1119. }
  1120.  
  1121.  
  1122. void DGopher::SetLink( char ctype, const char* link)
  1123. {
  1124.     // link data expected is pointer to standard gopher info line:
  1125.     // 0Title<tab>path<tab>host<tab>port<tab>plus...etc...<null | cr-lf>
  1126.     char    *cp, *cp0;
  1127.  
  1128.     DeleteLink(true); // clean out any old link info
  1129.  
  1130.     fLinkLen= StrLen( link) + 1;
  1131.     fLink = (char*) MemDup( (void*)link, fLinkLen);
  1132.     cp= cp0= fLink;
  1133.     fType= ctype; //= *cp;  -- we may be overriding link type
  1134.     fItitle= 1; // title always starts at 2nd char in link
  1135.     cp= StringChr(cp, '\t'); if (cp) { *cp++= 0; fIpath= cp-cp0; } else fIpath= 0;
  1136.     cp= StringChr(cp, '\t'); if (cp) { *cp++= 0; fIhost= cp-cp0; } else fIhost= 0;
  1137.     cp= StringChr(cp, '\t'); if (cp) { *cp++= 0; fIport= cp-cp0; } else fIport= 0;
  1138.     cp= StringChr(cp, '\t'); if (cp) { *cp++= 0; fIplus= cp-cp0; } else fIplus= 0;
  1139.  
  1140.     fIsLocal= ( fIhost==0 
  1141.                             || StrICmp(fLink+fIhost,kLocalhost) == 0  
  1142.                             || StrICmp(fLink+fIhost,gEmptyString) == 0 );
  1143.     
  1144.     if (fIport) fPort= (short)atol( cp0 + fIport); else fPort= kGopherport; // ???
  1145.         
  1146.     // look for Date+Size in title as
  1147.     // "File listing  [14Apr92, 58kb]" is an example 
  1148.     cp= cp0 + fItitle;
  1149.     char *dp= StringRChr(cp, ']');
  1150.     if (dp) {
  1151.         char *sz= dp-3;
  1152.         if (isdigit(*sz) && sz[1] == 'k' && sz[2] == 'b') {
  1153.             char *db= StrRChr(cp, '[');
  1154.             if (db && db < dp) {
  1155.                 char *te= db-1; // terminate title
  1156.                 while (te > cp0 && isspace(*te)) te--;
  1157.                 *(te+1)= 0;
  1158.                 db++; //*db++= 0; 
  1159.                 fIdate= db - cp0;
  1160.                 while (isdigit(*sz)) sz--;
  1161.                 fIsize= sz+1 - cp0;
  1162.                 while (db<sz && *db!=',') db++;
  1163.                 *db= 0; // terminate date
  1164.                 *dp= 0; // terminate size
  1165.                 if (!fDateLong) { 
  1166.                     long yr = atol(cp0+fIdate+5); 
  1167.                     long day= atol(cp0+fIdate);
  1168.                     long mon= 0; 
  1169.                     while (mon<12 && strncmp(gMonths[mon], cp0+fIdate+2, 3) != 0) mon++;
  1170.                     fDateLong= 1900000000 + yr * 1000000 + mon * 10000
  1171.                                 + day * 100; 
  1172.                     }
  1173.                 if (!fSizeLong) {
  1174.                     fSizeLong= atol(cp0 + fIsize);
  1175.                     char snum[50];
  1176.                     if (fSizeLong) sprintf( snum, "%luk", fSizeLong);
  1177.                     else strcpy(snum,"<1k");
  1178.                     if (fDataSize) MemFree(fDataSize);
  1179.                     fDataSize= (char*) StrDup( snum);
  1180.                     }
  1181.                 }
  1182.             }
  1183.         }
  1184. }
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190. void DGopher::ToText( CharPtr& h, short indent)
  1191. {    
  1192.     
  1193.     if (TRUE) { // if (fIsPlus) 
  1194.         char quote, *bufp, *buf, *plus;
  1195.             // RISKY having buf at fixed maxlen!, we need better way than sprintf(fixedbuf,...) !!
  1196.         long len= MAX( fLinkLen+1, 2048); 
  1197.         buf = (char*)MemNew(len);
  1198.         if (indent<0) indent= 0;
  1199.         if (indent) MemFill( buf, ' ', indent);
  1200.         bufp= buf+indent;
  1201.         
  1202.         if (fIsPlus) plus= "\t+"; else plus= "";
  1203.         sprintf( bufp, "+INFO: %c%s\t%s\t%s\t%d%s\n",
  1204.                 fType, (char*) GetName(), (char*) GetPath(), (char*) GetHost(), fPort, plus);
  1205.         StrExtendCat( &h, buf);
  1206.  
  1207.         if (fURL && fProtocol != kGopherprot) {
  1208.             //if (StrChr(fURL, '"')) quote= '\''; else quote= '"';
  1209.             quote= ' ';
  1210.             sprintf( bufp, "+URL: %c%s%c\n", quote,fURL,quote); 
  1211.             StrExtendCat( &h, buf);
  1212.             }
  1213.         
  1214.         // must store +MENU, +MENUSTRING, +QUERYSTRING and possibly other G+ block info
  1215.         // do we care to save +ADMIN, +VIEWS, +ASK, ... ???
  1216.  
  1217.         if (HasDocLink()) {
  1218.             short atparag, atchar;
  1219.             switch ( fDocLink->Kind()) {
  1220.                             
  1221.             case DocLink::kNoLink:
  1222.                 break;
  1223.                 
  1224.             case DocLink::kLine: {
  1225.                 short endparag, endchar;
  1226.                 fDocLink->GetLine( atparag, atchar, endparag, endchar);
  1227.                 sprintf( bufp, "+MENULINE: %d %d %d %d\n",
  1228.                                  atparag, atchar, endparag, endchar); 
  1229.                 StrExtendCat( &h, buf);
  1230.                 }
  1231.                 break;
  1232.  
  1233.             case DocLink::kLineRect: {
  1234.                 Nlm_RecT r;
  1235.                 fDocLink->GetLineRect( r, atparag, atchar);
  1236.                 sprintf( bufp, "+MENULINERECT: %d %d %d %d %d %d\n",
  1237.                                     atparag, atchar, r.left, r.top, r.right, r.bottom); 
  1238.                 StrExtendCat( &h, buf);
  1239.                 }
  1240.                 break;
  1241.  
  1242.             case DocLink::kRect: {
  1243.                 Nlm_RecT r;
  1244.                 fDocLink->GetRect( r);
  1245.                 sprintf( bufp, "+MENURECT: %d %d %d %d\n",
  1246.                                      r.left, r.top, r.right, r.bottom); 
  1247.                 StrExtendCat( &h, buf);
  1248.                 }
  1249.                 break;
  1250.                 
  1251.             case DocLink::kString: {
  1252.                 short skipval = 0;
  1253.                 char* links= fDocLink->GetString( skipval);
  1254.                 if (StrChr(links, '"')) quote= '\''; else quote= '"';
  1255.                 if (skipval)
  1256.                     sprintf( bufp, "+MENUSTRING: %c%s%c %d\n", quote, links, quote, skipval); 
  1257.                 else
  1258.                     sprintf( bufp, "+MENUSTRING: %c%s%c\n", quote,links,quote); 
  1259.                 StrExtendCat( &h, buf);
  1260.                 }
  1261.             break;
  1262.             
  1263.             }
  1264.     
  1265.         }
  1266.             
  1267.         if (fQueryGiven) {
  1268.             if (StrChr(fQueryGiven, '"')) quote= '\'';
  1269.             else quote= '"';
  1270.             sprintf( bufp, "+QUERYSTRING: %c%s%c\n", quote,fQueryGiven,quote); 
  1271.             StrExtendCat( &h, buf);
  1272.             }
  1273.             
  1274.         if (fGoMenuBlock) {
  1275.             // ...a bit of recursion...
  1276.             char* menutext= fGoMenuBlock->fGolist->WriteLinks(indent+1);
  1277.             sprintf(bufp, "+MENU:\n");
  1278.             StrExtendCat( &h, buf);
  1279.             StrExtendCat( &h, menutext);
  1280.             MemFree( menutext);
  1281.             }
  1282.  
  1283.         MemFree(buf);
  1284.         }
  1285.     else {
  1286.         char *buf = (char*)MemNew(fLinkLen+1);
  1287.         sprintf( buf, "%c%s\t%s\t%s\t%d\n",
  1288.                 fType, (char*) GetName(), (char*) GetPath(), (char*) GetHost(), fPort);
  1289.         StrExtendCat( &h, buf);
  1290.         MemFree(buf);
  1291.         }
  1292. }
  1293.  
  1294.  
  1295. void DGopher::ToServerText( CharPtr& h, short itemNum)
  1296. {
  1297.     long    maxlen= 7 * 6 + fLinkLen + 600; // is this safely more than we need?
  1298.     char     *buf= new char[ maxlen];
  1299.     char    *cp= buf;
  1300.     char  quote, cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1301.     char*    stmp;
  1302.         
  1303.     sprintf( cp, "Name=%s%s", (char*) GetName(),LineEnd); cp += strlen(cp);  
  1304.     if (itemNum) { sprintf( cp, "Numb=%d%s", itemNum,LineEnd); cp += strlen(cp); }
  1305.     sprintf( cp, "Type=%c%c%s", fType, cplus,LineEnd); cp += strlen(cp);
  1306.     sprintf( cp, "Host=%s%s", (char*) GetHost(),LineEnd);  cp += strlen(cp);  
  1307.     sprintf( cp, "Port=%d%s", fPort,LineEnd); cp += strlen(cp);
  1308.     sprintf( cp, "Path=%s%s", (char*) GetPath(),LineEnd); cp += strlen(cp);
  1309.     StrExtendCat( &h, buf);
  1310.     
  1311.     if (fAdminEmail) { 
  1312.         sprintf( buf, "Admin=%s%s", fAdminEmail,LineEnd);  
  1313.         StrExtendCat( &h, buf);
  1314.         }
  1315.     stmp= (char*)GetDate(); 
  1316.     if (stmp && *stmp) {
  1317.         sprintf(buf, "#+Mod-Date:%s%s", stmp,LineEnd);
  1318.         StrExtendCat( &h, buf);
  1319.         }
  1320.  
  1321.     if (fURL) {
  1322.         //if (StrChr(fURL, '"')) quote= '\''; else quote= '"';
  1323.         quote= ' ';
  1324.         sprintf( buf, "+URL: %c%s%c\n", quote,fURL,quote); 
  1325.         StrExtendCat( &h, buf);
  1326.         }
  1327.  
  1328. #if 0
  1329.     if (fIsPlus == kGopherPlusYes) {
  1330.         sprintf( buf, "#+Views:%s",LineEnd);
  1331.         StrExtendCat( &h, buf);
  1332.         CArrayIterator iter( fViews);
  1333.         void*     iview;
  1334.         for (iview = iter.First(); iter.More(); iview = iter.Next())  {
  1335.             stmp= (*(DGopherItemView**) iview)->ViewVal();
  1336.             sprintf( buf, "# %s%s",  (char*)stmp,LineEnd);
  1337.             StrExtendCat( &h, buf);
  1338.             }
  1339.         }
  1340. #endif
  1341.  
  1342.     sprintf( buf, "#%s",LineEnd); 
  1343.     StrExtendCat( &h, buf);
  1344.     delete[] buf;
  1345. }
  1346.  
  1347.  
  1348.  
  1349. void DGopher::ToPrettyText( CharPtr& h, short /*itemNum*/,
  1350.                 Boolean showDate, Boolean showSize, Boolean showKind, Boolean showPath, 
  1351.                 Boolean showHost, Boolean showPort, Boolean showAdmin)
  1352. {
  1353.     char*    stmp;
  1354.     long    maxlen= 8 * 6 + fLinkLen + 600; // is this safely more than we need?
  1355.     char     *buf= new char[ maxlen];
  1356.     char    *cp= buf;
  1357.     char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1358.     
  1359. #define SprintIfReal(WHAT)    \
  1360.     if (stmp && *stmp) sprintf( cp, "\t%s", stmp); else sprintf( cp, "\t"); \
  1361.     cp += strlen(cp);     
  1362.     
  1363.     stmp= (char*)GetName(); sprintf( cp, "%s", stmp); cp += strlen(cp); 
  1364.     if (showDate) { stmp= (char*)GetDate(); SprintIfReal("Date"); }
  1365.     if (showSize) { stmp= (char*)GetDataSize(); SprintIfReal("Size"); }
  1366.     if (showKind) { sprintf( cp, "\t%c%c", fType, cplus); cp += strlen(cp); }
  1367.     if (showPath) { stmp= (char*)GetPath(); SprintIfReal("Path"); }
  1368.     if (showHost) { stmp= (char*)GetHost(); SprintIfReal("Host"); }
  1369.     if (showPort) { sprintf( cp, "\t%d", fPort); cp += strlen(cp); }
  1370.     if (showAdmin && fAdminEmail) { stmp= fAdminEmail; SprintIfReal("Admin"); }
  1371.     sprintf( cp, "%s",LineEnd); cp += strlen(cp);
  1372.     StrExtendCat( &h, buf);
  1373.     
  1374.     delete[] buf;
  1375. }
  1376.  
  1377. void DGopher::StoreName(char* aStr)
  1378. {
  1379.     long len= 100 + fLinkLen + StrLen(aStr);
  1380.     char *link = (char*) MemNew(len);
  1381.     char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1382.     sprintf( link, "%c%s\t%s\t%s\t%d\t%c",
  1383.         fType, aStr/*(char*) GetName()*/, (char*) GetPath(), (char*) GetHost(), fPort, cplus);
  1384.     SetLink( fType, link);
  1385.     MemFree( link);
  1386. }
  1387.  
  1388. void DGopher::StorePath(char* aStr)
  1389. {
  1390.     long len= 100 + fLinkLen + StrLen(aStr);
  1391.     char *link = (char*) MemNew(len);
  1392.     char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1393.     sprintf( link, "%c%s\t%s\t%s\t%d\t%c",
  1394.         fType, (char*) GetName(), aStr/*(char*) GetPath()*/, (char*) GetHost(), fPort, cplus);
  1395.  
  1396.     SetLink( fType, link);
  1397.     MemFree( link);
  1398. }
  1399.  
  1400. void DGopher::StoreHost(char* aStr)
  1401. {
  1402.     long len= 100 + fLinkLen + StrLen(aStr);
  1403.     char *link = (char*) MemNew(len);
  1404.     char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1405.     if (!aStr || !*aStr || StrICmp(aStr,gEmptyString) == 0) 
  1406.         aStr= (char*)kLocalhost;
  1407.     sprintf( link, "%c%s\t%s\t%s\t%d\t%c",
  1408.         fType, (char*) GetName(), (char*) GetPath(), aStr/*(char*) GetHost()*/, fPort, cplus);
  1409.     SetLink( fType, link);
  1410.     MemFree( link);
  1411.     // done in SetLink: fIsLocal= (StrICmp(aStr,kLocalhost) == 0 ); // fProtocol == kFileprot && 
  1412. }
  1413.  
  1414. void DGopher::StorePort(char* aStr)
  1415. {
  1416.     long len= 100 + fLinkLen + StrLen(aStr);
  1417.     char *link = (char*) MemNew(len);
  1418.     char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1419.     sprintf( link, "%c%s\t%s\t%s\t%s\t%c",
  1420.         fType, (char*) GetName(), (char*) GetPath(), (char*) GetHost(), aStr/*fPort*/, cplus);
  1421.     SetLink( fType, link);
  1422.     MemFree( link);
  1423. }
  1424.  
  1425.  
  1426. // static
  1427. short DGopher::StandardPort( short protocol)
  1428. {
  1429.     switch (protocol) {
  1430.         case kGopherprot: return kGopherport;
  1431.         case kHTTPprot    : return kHTTPport;
  1432.         case kSMTPprot    :    return kSMTPport;
  1433.         case kFileprot    : return kFileport;
  1434.         case kFTPprot        : return kFTPport;
  1435.         case kTelnetprot: return kTelnetport;
  1436.         case kTN3270prot: return kTN3270port;
  1437.         case kWhoisprot    : return kWhoisport;
  1438.         case kFingerprot: return kFingerport;
  1439.         case kPOPprot        : return kPOPport;
  1440.         case kNNTPprot    : return kNNTPport;
  1441.         case kWAISprot    : return kWAISport;
  1442.  
  1443.         case kUnsupportedProt: return kUnsupportedPort;
  1444.         default:
  1445.         case kUnknownProt: return kUnknownPort;
  1446.         }
  1447. }
  1448.  
  1449. void DGopher::StoreProtocol( short protocol)
  1450. {
  1451.     fProtocol= protocol;
  1452.     fPort= StandardPort( fProtocol);
  1453.     switch (fProtocol) {    
  1454.             
  1455.         case  kGopherprot    :  
  1456.         case  kHTTPprot        :  
  1457.         case  kFTPprot        : 
  1458.         case  kFileprot        : 
  1459.             // need to flag if localhost !? -- for all prots ??
  1460.             fIsLocal= (StrICmp(GetHost(),kLocalhost) == 0 
  1461.                             || StrICmp(GetHost(),gEmptyString) == 0 );
  1462.             break;
  1463.             
  1464.             // currently unsupported
  1465.         case  kNNTPprot        : 
  1466.         case  kWAISprot        :  
  1467.         case  kTelnetprot    :  
  1468.         case  kTN3270prot    : 
  1469.         case  kUnknownProt:
  1470.         case  kUnsupportedProt:
  1471.             fType= kTypeError; // ??
  1472.             break;
  1473.         }
  1474. }
  1475.  
  1476.  
  1477. const char* DGopher::GetProtocol()
  1478. {
  1479. #if 0
  1480.     short item= DGopher::GetProtoItem(fProtocol);
  1481.     if (item<0) return "unknown://";
  1482.     return DGopher::GetProtoName(item);
  1483. #else
  1484.     switch (fProtocol) {
  1485.         case  kNNTPprot        : return "news:"; 
  1486.         case  kSMTPprot        : return "mailto:"; 
  1487.         case  kGopherprot    : return "gopher://"; 
  1488.         case  kHTTPprot        : return "http://"; 
  1489.         case  kFTPprot        : return "ftp://";  // should be same as file:// ??
  1490.         case  kFileprot        : return "file://"; 
  1491.         case  kWAISprot        : return "wais://"; 
  1492.         case  kTelnetprot    : return "telnet://"; 
  1493.         case  kTN3270prot    : return "tn3270://"; 
  1494.         
  1495.         case  kUnknownProt: 
  1496.         case  kUnsupportedProt: 
  1497.         default: 
  1498.                 return "unknown://";                
  1499.         }
  1500. #endif
  1501. }
  1502.  
  1503. void DGopher::StoreURL(char* aStr)
  1504. {
  1505.     fURL= (char*) MemFree(fURL); 
  1506.     if (aStr) fURL= StrDup(aStr);
  1507. }
  1508.  
  1509. const char* DGopher::GetURL()
  1510. {
  1511. //    sprintf( buf, "URL: gopher://%s:%d/%c/%s%s", 
  1512. //                    (char*) GetHost(), fPort, fType, (char*) GetPath(),LineEnd);
  1513.     if (!fURL) {
  1514.         char cstore[512];
  1515.         char* url= StrDup( GetProtocol());
  1516.         StrExtendCat( &url, (char*) GetHost());
  1517.         if (fPort != StandardPort(fProtocol)) {
  1518.             sprintf( cstore, ":%d", fPort);
  1519.             StrExtendCat( &url, cstore);
  1520.             }
  1521.         
  1522.         char* epath= DURL::EncodeChars( GetPath());
  1523.         switch (fProtocol) {
  1524.             case kGopherprot:
  1525.                 sprintf( cstore, "/%c%s", fType,epath);
  1526.                 StrExtendCat( &url, cstore);
  1527.                 break;
  1528.                 
  1529.              case kFileprot:
  1530.                 if (*epath != '/') {
  1531.                     StrExtendCat( &url, "/");
  1532.                     // ?? do some fiddling w/ *path ???
  1533.                     }
  1534.             default:
  1535.                 StrExtendCat( &url, epath);
  1536.                 break;
  1537.             }
  1538.             
  1539.         MemFree( epath);
  1540.         
  1541.         if (fQueryGiven) {
  1542.             StrExtendCat( &url, "?");
  1543.             StrExtendCat( &url, fQueryGiven);
  1544.             }
  1545.             
  1546.         fURL= url;
  1547.         }
  1548.     return fURL;
  1549. }
  1550.  
  1551.  
  1552. void DGopher::StorePlus(char *plus)
  1553. {
  1554.     long len= 10 + fLinkLen + 1;
  1555.     char *link = (char*) MemNew(len);
  1556.     //char cplus = (fIsPlus == kGopherPlusYes) ? '+' : ' ';
  1557.     sprintf( link, "%c%s\t%s\t%s\t%d\t%c",
  1558.         fType, (char*) GetName(), (char*) GetPath(), (char*) GetHost(), fPort, *plus);
  1559.     SetLink( fType, link);
  1560.     MemFree( link);
  1561. }
  1562.  
  1563. void DGopher::StorePlus(Boolean isplus)
  1564. {
  1565.     if (isplus) { 
  1566.         fIsPlus = kGopherPlusYes; 
  1567.         StorePlus("+");
  1568.         }
  1569.     else { 
  1570.         fIsPlus = kGopherPlusNo; 
  1571.         StorePlus("");
  1572.         }
  1573. }
  1574.  
  1575.  
  1576.  
  1577. Boolean DGopher::Edit(long dialogID)
  1578. {
  1579.     Boolean            result= false;
  1580.  
  1581.     DGopherEditWindow *aWind= new DGopherEditWindow(this, dialogID != 0);
  1582.     result= aWind->PoseModally();
  1583.     delete aWind;
  1584.     // aWind handles all of rest -- okay button causes this gopher to be updated
  1585.     
  1586.     return result;
  1587. }
  1588.  
  1589.  
  1590. Boolean DGopher::Equals(DGopher* other)
  1591. {
  1592.     if (!other) return false;
  1593.     else if (fType != other->fType) return false;
  1594.     else if (fPort != other->fPort) return false;
  1595.     else if (fProtocol != other->fProtocol) return false;
  1596.     else if (StringCmp( fLink+fIhost, other->fLink+other->fIhost)) return false;
  1597.     else if (StringCmp( fLink+fIpath, other->fLink+other->fIpath)) return false;
  1598.     else if (StringCmp( fQueryGiven, other->fQueryGiven)) return false;
  1599.     else return true;
  1600. }
  1601.  
  1602. Boolean DGopher::operator ==(DGopher* other)
  1603. {
  1604.     return Equals(other);
  1605. }
  1606.             
  1607.  
  1608. void DGopher::SwapPathType(char oldtype, char newtype)
  1609. {
  1610.     // this is very risky as protocol says path is opaque to client -- should drop.
  1611.     // want it to set binary/text transfer protocol for server
  1612.     if (*(fLink+fIpath) == oldtype) *(fLink+fIpath)= newtype;
  1613. }
  1614.  
  1615.  
  1616. void DGopher::SetOwner( DGopherList* OwnerList)  
  1617. {
  1618.     fOwnerList= OwnerList;
  1619.     if (fOwnerList) fStatusLine= fOwnerList->fStatusLine; // ?? is this only use of fOwnerList?
  1620.     gNeedTypeChange= false;
  1621.     
  1622.     if (gDoSuffix2MacMap && gGopherMap) {
  1623.         DGopherMap* mapper;
  1624.         mapper= gGopherMap->MatchGopherType( this);
  1625.         if (mapper) gNeedTypeChange |= mapper->Map(this);
  1626.         mapper= gGopherMap->MatchSuffix( this);
  1627.         if (mapper) gNeedTypeChange |= mapper->Map(this);
  1628.         }
  1629. }
  1630.  
  1631.  
  1632.  
  1633. const char* DGopher::GetKindName() 
  1634.     return "Unknown"; 
  1635. }
  1636.  
  1637. const char* DGopher::GetName()   
  1638. {
  1639.     if (fItitle) return  fLink + fItitle;
  1640.     else return gEmptyString;
  1641. }
  1642.  
  1643. const char* DGopher::GetDate()   
  1644. {
  1645.     if (fDate) return fDate;
  1646.     else if (fIdate) return  fLink + fIdate;
  1647.     else return gEmptyString;
  1648. }
  1649.  
  1650. const char* DGopher::GetDataSize()   
  1651. {
  1652.     if (fDataSize) return fDataSize;
  1653.     else if (fIsize) return fLink + fIsize;
  1654.     else return gEmptyString;
  1655. }
  1656.  
  1657. const char* DGopher::GetPath()   
  1658. {
  1659.     if (fIpath) return fLink + fIpath;
  1660.     else return gEmptyString;
  1661. }
  1662.  
  1663. const char* DGopher::GetHost()   
  1664. {
  1665.     if (fIhost) return fLink + fIhost;
  1666.     else return gEmptyString;
  1667. }
  1668.  
  1669. const char* DGopher::GetPort()   
  1670. {
  1671.     if (fIport) return fLink + fIport;
  1672.     else return gEmptyString;
  1673. }
  1674.  
  1675. const char* DGopher::GetPlus()   
  1676. {
  1677.     if (fIplus) return fLink + fIplus;
  1678.     else return gEmptyString;
  1679. }
  1680.  
  1681.  
  1682. const char* DGopher::ShortName()   
  1683. {
  1684.     static char  shorty[33];
  1685.     if (fItitle) {
  1686.         strncpy(shorty, fLink + fItitle, 32);
  1687.         shorty[33]= 0;
  1688.         return shorty;
  1689.         }
  1690.     else return gEmptyString;
  1691. }
  1692.  
  1693. void DGopher::ReadALine() 
  1694. // this is here for easy subclassing to process line as read thru talker 
  1695. {
  1696.     char *data = NULL;
  1697.  
  1698.     if (fIsLocal && fReplyFile) {
  1699.         char dline[1024];
  1700.         ulong datalen= 1024;
  1701.         fReplyFile->ReadLine( dline, datalen);
  1702.         if (datalen>0) {
  1703.             dline[datalen]= 0;
  1704.             if (fInfo == NULL) {
  1705.                 fInfo= (char*)MemDup(dline, datalen+1);
  1706.                 fInfoSize= datalen;
  1707.                 }
  1708.             else {
  1709.                 MemExtendCat( &fInfo, fInfoSize, dline, datalen+1); // +1 for 0
  1710.                 fInfoSize += datalen;
  1711.                 }
  1712.             }
  1713.  
  1714.         }
  1715.     else if (fTalker && fTalker->ReadALine( NULL, data)) {
  1716.                                 // this changes w/ subclasses...
  1717.         if ( data[0] == '.' && data[1] == kCR ) 
  1718.             fTalker->SetEndOfMessage(true);
  1719.         if (!fTalker->EndOfMessage()) {
  1720.             long datalen= StrLen(data);
  1721.             if (fInfo == NULL) {
  1722.                 fInfo= data;
  1723.                 fInfoSize= datalen;
  1724.                 }
  1725.             else {
  1726.                 MemExtendCat( &fInfo, fInfoSize, data, datalen+1); // +1 for 0
  1727.                 fInfoSize += datalen;
  1728.                 MemFree( data);
  1729.                 }
  1730.             }
  1731.         }
  1732. }
  1733.  
  1734.  
  1735. Boolean DGopher::EndOfMessage()
  1736. // this is here for easy subclassing to process line as read thru talker 
  1737. {
  1738.     return fTalker->EndOfMessage();
  1739. }
  1740.  
  1741.  
  1742.  
  1743. void DGopher::ShowMessage(const char* msg)
  1744. {
  1745.     if (fStatusLine) fStatusLine->SetTitle( (char*)msg);
  1746. }
  1747.  
  1748. void DGopher::ShowProgress()
  1749. {
  1750.     char snum[40], buf[128];
  1751.     Dgg_LongToStr( fBytesReceived, snum, 0, 40);
  1752.     sprintf( buf, "Bytes sent: %s", snum);
  1753.     long csec = fConnectTime; // time / 60;
  1754.     if (csec > 0) {
  1755.         char buf2[128];
  1756.         csec= fBytesReceived / csec;
  1757.         Dgg_LongToStr( csec, snum, 0, 40);
  1758.         sprintf( buf2, "%s, bytes/sec: %s", buf, snum);
  1759.         ShowMessage( buf2);
  1760.         }
  1761.     else
  1762.         ShowMessage( buf);
  1763. }
  1764.  
  1765.  
  1766.  
  1767. void DGopher::FinishRead()
  1768. {
  1769. }
  1770.  
  1771. const char* DGopher::GetViewChoiceKind()
  1772. {
  1773.     if (fViewchoice > 0 && fViews && (fViews->GetSize()>0)) {
  1774.         DGopherItemView* giv= (DGopherItemView*) fViews->At(fViewchoice-1);
  1775.         if (giv) return giv->Kind();
  1776.         }
  1777.     return NULL; // or use gGopherMap & fType to return default Kind ??
  1778. }
  1779.  
  1780. void DGopher::CheckViewMappers()
  1781. {
  1782.     if (fViews) {        
  1783.         DGopherItemView* giv;
  1784.         unsigned short maxrank= 0;
  1785.         long i, maxat= 0, nviews= fViews->GetSize();
  1786.         char* cp;
  1787.         for (i= 0; i<nviews; i++) {
  1788.             char * viewkind;
  1789.             unsigned short lastrank = 0; 
  1790.             giv= (DGopherItemView*) fViews->At(i);
  1791.             viewkind= (char*)giv->Kind();
  1792.             DGopherMap* aMapper= gGopherMap->MatchHandlerKind( lastrank, viewkind); 
  1793.             if (aMapper) {
  1794.                 giv->fViewStatus= lastrank;
  1795.                 if (lastrank > maxrank) { maxrank= lastrank; maxat= i; }
  1796.                 }
  1797.             else
  1798.                 giv->fViewStatus= DGopherItemView::kUnknownStatus;
  1799.             }
  1800.         giv= (DGopherItemView*) fViews->At(maxat);
  1801.         giv->fViewStatus= DGopherItemView::kMaxStatus;
  1802.         if (maxrank>0) fViewchoice= maxat + 1; //???
  1803.         }
  1804. }
  1805.  
  1806.  
  1807.  
  1808. #if 0
  1809.  
  1810. // LocalGopher.....
  1811.  
  1812. types of calls to handle locally:
  1813.  
  1814.     for all calls
  1815.         x-- if StrChr( GetPath(), '/') && !defined(OS_UNIX)
  1816.            do filesystem path translation
  1817.         x-- must tack on gGopherRoot to top of path ??
  1818.         
  1819.   for any app call
  1820.       x-- add -v view/choice cmdline param ?
  1821.   
  1822.   if (path == "R123-456-path/to/data")
  1823.       x-- handle data file subrange
  1824.       
  1825.     if (fType == kTypeFolder) 
  1826.         || StrICmp( viewchoice,"application/gopher-menu") == 0
  1827.         || StrICmp( viewchoice,"application/gopher+-menu")==0
  1828.         )
  1829.         -- do local-filesystem directory to gopher menu translation
  1830.         -- some of these types may be to call apps instead (exec+, ?query)
  1831.         
  1832.   if (fQuery) 
  1833.       -- assume path is to local executable? 
  1834.       x-- may be path wais index.{src,dct,inv} instead !?
  1835.          ?? can we handle this with call to apps/askwais -d path/to/index ??
  1836.       x-- call DChildApp::Launch() w/ fQuery part of cmdline
  1837.       
  1838.   if (fType == kTypeQuery)
  1839.       x-- assume path is to local executable? 
  1840.       x-- may be path wais index.{src,dct,inv} instead !?
  1841.         -- should always have (fQuery) ?
  1842.  
  1843.     else if (fTypes == othertype)
  1844.          
  1845.          kTypeFile -- read & display local file 
  1846.          kTypeBinary 
  1847.          kTypeSound  
  1848.          kTypeImage 
  1849.          kTypeMovie 
  1850.          kTypeGif         
  1851.          kTypeHtml   
  1852.          others -- no special handling
  1853.  
  1854.   if (StrNICmp( GetPath(), "exec+:", 6)==0)  
  1855.       x-- assume path is to local executable
  1856.       x-- cut cmdline params from exec+:params:/path/to/app
  1857.       x-- call DChildApp::Launch()  
  1858.       
  1859.   if (fQueryPlus)
  1860.       -- this is typically either "\t+" or "\t$" or "\t!" or esp. fReplyData
  1861.     -- this is of low importance?
  1862.     
  1863.   if (fHaveReply) // for ASK forms, 
  1864.       x-- see fQueryPlus, but also may have fReplyFile
  1865.       x-- assume path is to app, call DChildApp::Launch()
  1866.    
  1867. #endif
  1868.  
  1869.  
  1870.  
  1871.  
  1872.   
  1873. void DGopher::LocalQuery( char* viewchoice)
  1874. {
  1875.     enum actions { kDoNothing, kShowFile, kShowFileSection, kShowFolder, 
  1876.                                 kLaunchApp, kLaunchWais };
  1877.     short action = kDoNothing, kind;
  1878.     char * Stdinbuf = NULL, * Stdinfile= NULL;
  1879.     char * pathname = StrDup( (char*) GetPath()); 
  1880.     char * cmdline  = StrDup("");
  1881.     char * shortpath = pathname, * thepath= pathname;
  1882.     long     rootlen= 0;
  1883.     char * cp, * dp;
  1884.     long   rstart = 0, rend = 0;
  1885.     ulong filelen = 0;
  1886.     
  1887.      fTalker= NULL;
  1888.     fBytesExpected= 0; //kReadToClose;
  1889.     fBytesReceived= 0;
  1890.     fConnectTime= 0;
  1891.  
  1892.     if (!viewchoice && (fType == kTypeHtml || fProtocol == kHTTPprot)) {
  1893.         viewchoice= "text/html";
  1894.         }
  1895.  
  1896.     if (viewchoice) {
  1897.         StrExtendCat( &cmdline, " -v ");
  1898.         StrExtendCat( &cmdline, viewchoice);
  1899.         }
  1900.  
  1901.   if (StrNICmp( pathname, "exec+:", 6)==0) {
  1902.       char * cmds, * ep;
  1903.       cmds= pathname+6;
  1904.       ep= StrChr( cmds, ':');
  1905.       if (ep) {
  1906.           *ep++= 0;
  1907.             StrExtendCat( &cmdline, " ");
  1908.              // ! this is risky, but how else do we handle paths in the cmdline ??
  1909.             DFileManager::UnixToLocalPath( cmds); 
  1910.           StrExtendCat( &cmdline, cmds);
  1911.           Nlm_MemMove( pathname, ep, 1+StrLen(ep));
  1912.           }
  1913.         action= kLaunchApp;     
  1914.         }
  1915.     else if (*pathname == 'R' 
  1916.         && isdigit(pathname[1])
  1917.         && (cp= StrChr(pathname, '-')) != NULL
  1918.         && isdigit(cp[1])
  1919.         && (dp= StrChr(cp+1, '-')) > cp ) { 
  1920.             //handle doc subrange paths like 'R851270-852774-:Flybase:genes:genes.txt'
  1921.             rstart= atol( pathname+1);
  1922.             rend  = atol( cp+1);
  1923.             dp++;
  1924.           Nlm_MemMove( pathname, dp, 1+StrLen(dp));
  1925.             action= kShowFileSection;
  1926.             }
  1927.         
  1928. #if 0
  1929.   else if (StrNICmp( pathname, "waissrc:", 6)==0) {
  1930.         // ?? kLaunchWais on local wais src ??
  1931.         }
  1932. #endif        
  1933.  
  1934.     kind= DFileManager::FileOrFolderExists( pathname);
  1935.     if (kind==0) {
  1936.         DFileManager::UnixToLocalPath( pathname);
  1937.         kind= DFileManager::FileOrFolderExists( pathname);
  1938.         if (kind==0) {
  1939.             StrPrependCat( &pathname, gLocalGopherRoot);
  1940.             rootlen= StrLen(gLocalGopherRoot);
  1941.             shortpath= pathname + rootlen;
  1942.             thepath= pathname;
  1943.             kind= DFileManager::FileOrFolderExists( pathname);
  1944.             }
  1945.         }
  1946.     
  1947.     if (kind == DFileManager::kIsFolder) {
  1948.         fType= kTypeFolder; // ??
  1949.         action= kShowFolder;
  1950.         }    
  1951.         
  1952.     if (action == kDoNothing && fType == kTypeQuery && fQuery) {
  1953.         if ( DFileManager::FileExists(pathname) )
  1954.             action= kLaunchApp;
  1955.         else if ( DFileManager::FileExists(shortpath) ) {
  1956.             thepath= shortpath;
  1957.             action= kLaunchApp;
  1958.             }
  1959.         else {
  1960.             char path2[512];
  1961.             StrNCpy( path2, pathname, sizeof(path2));
  1962.             StrNCat( path2, ".src", sizeof(path2));
  1963.             if (DFileManager::FileExists( path2)) {
  1964.                 action= kLaunchWais;
  1965.                 // add default cmdline for AskWais...
  1966. #if 1
  1967.                 sprintf( path2, " -+i -d %s", pathname);
  1968. #else
  1969.                 sprintf( path2, " -+i -g %s -d %s", gLocalGopherRoot, pathname);
  1970. #endif
  1971.                 StrExtendCat( &cmdline, path2); 
  1972.                 }
  1973.             else if (DFileManager::FileExists( path2+rootlen)) {
  1974.                 action= kLaunchWais;
  1975. #if 1
  1976.                 sprintf( path2, " -+i -d %s", shortpath);
  1977. #else
  1978.                 sprintf( path2, " -+i -g %s -d %s", gLocalGopherRoot, shortpath);
  1979. #endif
  1980.                 StrExtendCat( &cmdline, path2); 
  1981.                 }
  1982.             }
  1983.         }
  1984.  
  1985.     if (fHasAsk && fHaveReply) {
  1986.         action= kLaunchApp;     
  1987.         if (fQueryPlus) Stdinbuf= fQueryPlus;
  1988.         else if (fReplyFile) Stdinfile= (char*) fReplyFile->GetName();
  1989.          }
  1990.     else if (fQueryPlus) {
  1991.         // typically either "\t+" or "\t$" or  "\t!"
  1992.         }
  1993.  
  1994.     if (fQuery) {
  1995.         *fQuery= ' '; // note: *fQuery == '\t', change to blank for cmdline
  1996.         StrExtendCat( &cmdline, " ");
  1997.         StrExtendCat( &cmdline, fQuery);
  1998.         if (action == kDoNothing) action= kLaunchApp; //?? is just fQuery enough to act this way?
  1999.         }
  2000.     
  2001.     if (action == kDoNothing)  switch (fType) {
  2002.         case kTypeFile  :  
  2003.         case kTypeBinary:  
  2004.         case kTypeSound :  
  2005.         case kTypeImage : 
  2006.         case kTypeMovie : 
  2007.         case kTypeGif        :  
  2008.         case kTypeHtml  :
  2009.             action= kShowFile; 
  2010.             break;  
  2011.         case kTypeFolder  :
  2012.             action= kShowFolder; 
  2013.             // ?? some subcall if fQueryPlus == $ or + or ! ??
  2014.             break;  
  2015.         }
  2016.  
  2017. #if 0
  2018.     // ???
  2019.     if (action == kDoNothing && ( viewchoice && 
  2020.         (StrICmp( viewchoice,"application/gopher-menu")==0
  2021.         || StrICmp( viewchoice,"application/gopher+-menu")==0 )
  2022.         ) ) {
  2023.         action= kShowFolder;
  2024.         }
  2025. #endif
  2026.  
  2027.     if (fQueryPlus && StrCmp(fQueryPlus,"\t!")==0) {
  2028.             // drop info calls for now ?
  2029.         action= kDoNothing;
  2030.         }
  2031.         
  2032.     switch (action) {
  2033.             
  2034.         case kShowFileSection: {
  2035.             fReplyFile= new DFile( thepath, "rb"); // don't forget "b" binary, or will fail w/ newline translation for bin files !!
  2036.           fReplyFile->Open("rb");
  2037.             //fReplyFile->GetDataLength(filelen); 
  2038.             fReplyFile->Seek( rstart);
  2039.             fBytesExpected= rend - rstart + 1; // ?? +1 ??  
  2040.             }
  2041.             break;
  2042.  
  2043.         case kShowFile: {
  2044.             fReplyFile= new DFile( thepath, "rb"); // don't forget "b" binary, or will fail w/ newline translation for bin files !!
  2045.           fReplyFile->Open("rb");
  2046.             fReplyFile->GetDataLength(filelen); 
  2047.             fBytesExpected= filelen;  
  2048.             }
  2049.             break;
  2050.             
  2051.         case kShowFolder: {
  2052. #if 0
  2053.     //debug
  2054. #include <DApplication.h>
  2055.             char* stdoutfile= StrDup("Gopup.localfolder");
  2056.             StrPrependCat( &stdoutfile, 
  2057.                 (char*)DFileManager::PathOnlyFromPath( gApplication->Pathname()));
  2058.             fReplyFile= new DFile(stdoutfile);  
  2059. #else
  2060.             fReplyFile= new DTempFile(); // disk file will be removed on delete
  2061. #endif
  2062.           fReplyFile->Open("w");
  2063.             DGopherList::LocalFolder2GopherList( fReplyFile, thepath);
  2064.           fReplyFile->Open("r");
  2065.             fReplyFile->GetDataLength(filelen); 
  2066.             fBytesExpected= filelen;  
  2067.             }
  2068.             break;
  2069.             
  2070.         case kLaunchApp: {
  2071.             DChildApp* child= new DChildApp( thepath, cmdline, true, Stdinfile, true); 
  2072.             if (Stdinbuf) 
  2073.                 child->AddInputBuffer( DChildFile::kStdin, Stdinbuf, StrLen(Stdinbuf));
  2074.             if (!child->Launch())
  2075.                 Message(MSG_OK, "Failed to launch %s with '%s'", thepath, cmdline);
  2076.             }
  2077.             break;
  2078.             
  2079.         case kLaunchWais: {
  2080.             if (DFileManager::FileExists( gAskWaisPath))
  2081.                 thepath= gAskWaisPath;
  2082.             else {
  2083.                 MemFree( pathname);
  2084.               pathname= StrDup( gLocalGopherRoot);
  2085.                 StrExtendCat( &pathname, gAskWaisPath );
  2086.                 thepath= pathname;
  2087.                 }
  2088. #if 0
  2089.     // debugging
  2090. #include <DApplication.h>
  2091.             char* stdoutfile= StrDup("Gopup.stdout");
  2092.             StrPrependCat( &stdoutfile, 
  2093.                 (char*)DFileManager::PathOnlyFromPath( gApplication->Pathname()));
  2094.             DChildApp* child= new DChildApp( thepath, cmdline, false, NULL, true); 
  2095.             DChildFile* cfile= new DChildFile( stdoutfile, DChildFile::kStdout,
  2096.                                         DChildFile::kDontDelete, DChildFile::kOpenText, "w");
  2097.             child->AddFile(cfile);
  2098.             MemFree(stdoutfile);
  2099. #else
  2100.             DChildApp* child= new DChildApp( thepath, cmdline, true, NULL, true); 
  2101. #endif
  2102.             if (!child->Launch())
  2103.                 Message(MSG_OK, "Failed to launch %s with '%s'", thepath, cmdline);
  2104.             }
  2105.             break;
  2106.             
  2107.         case kDoNothing:
  2108.         default:
  2109.             fBytesExpected= 0;
  2110.             break;
  2111.         }
  2112.                 
  2113.     fQueryPlus= NULL; // set to zero after each call?
  2114.     fQuery= NULL;
  2115.  
  2116.     MemFree( pathname);
  2117.     MemFree( cmdline);
  2118. }
  2119.  
  2120.  
  2121.  
  2122.  
  2123. void DGopher::OpenQuery()
  2124. {
  2125.     char* viewchoiceptr = NULL;
  2126.     char    viewchoicestore[256];
  2127.     
  2128.     if (fViewchoice > 0)
  2129.      if (fViews && fViews->GetSize()>1) {
  2130.         DGopherItemView* giv= (DGopherItemView*) fViews->At(fViewchoice-1);
  2131.         if (giv) {
  2132.             strcpy( viewchoicestore, giv->ViewRequest());
  2133.             viewchoiceptr= viewchoicestore;
  2134.             if (fQueryPlus && (0 == StrCmp(fQueryPlus,"\t+") || 0 == StrCmp(fQueryPlus,"\t$")))
  2135.                 fQueryPlus= NULL; // !! fix bug of sending <tab>+ after <tab>+viewchoice !!
  2136.             }
  2137.         }
  2138.         
  2139.     DeleteInfo();
  2140.     
  2141.     fThreadState= kThreadStarted;
  2142.     if (fIsLocal) {
  2143.         LocalQuery( viewchoiceptr);
  2144.         return;
  2145.         }
  2146.  
  2147.     fTalker= new DGopherTalk();  
  2148.     switch (fProtocol) {
  2149.  
  2150.         case kFingerprot: 
  2151.         case kWhoisprot: 
  2152.         case kGopherprot:
  2153.             fTalker->OpenQuery( fLink+fIhost, fPort, fLink+fIpath, fQuery, 
  2154.                                             viewchoiceptr, fQueryPlus, (fHaveReply)?fReplyFile:NULL);
  2155.             break;
  2156.  
  2157.         case kHTTPprot: {
  2158.             char * postdata = NULL;
  2159.             char * httppath= DURL::EncodeChars( GetPath());  // ?? possible double-encoding?
  2160.             StrPrependCat( &httppath, "GET ");
  2161.             if (fQuery) {
  2162.                 *fQuery= '?'; // note: *fQuery == '\t', change to '?' for http
  2163.                 StrExtendCat( &httppath, fQuery);
  2164.                 }
  2165.             if (fHaveReply) {
  2166.                 Boolean isHTMLPostForm= (fHasAsk == 5);
  2167.                 //Boolean isHTMLForm= (fHasAsk == 3 || fHasAsk == 5);
  2168.                 //fQueryPlus= fReplyData;   << this is set in ReadTask
  2169.                 if (isHTMLPostForm) {
  2170.                     postdata= fReplyData;
  2171.                     }
  2172.                 else { // GET form
  2173.                     StrExtendCat( &httppath, "?");
  2174.                     StrExtendCat( &httppath, fReplyData);
  2175.                     }
  2176.                 }
  2177.                 
  2178.             fTalker->OpenQuery( fLink+fIhost, fPort, httppath, NULL, 
  2179.                                             NULL, postdata, NULL);
  2180.             MemFree(httppath);
  2181.             }
  2182.             break;
  2183.         
  2184.             
  2185.         case kFTPprot:     // file:///path:to:file
  2186.         case kFileprot: 
  2187.         //?? kCSOprot:
  2188.         case kWAISprot:
  2189.         case kTelnetprot:
  2190.         case kTN3270prot:
  2191.         case kPOPprot:
  2192.         case kNNTPprot:
  2193.         case kSMTPprot:
  2194.         default:
  2195.             break;
  2196.             
  2197.         }
  2198.     fQueryPlus= NULL; // set to zero after each call?
  2199.     fQuery= NULL;
  2200.     fBytesExpected= kReadToClose;
  2201.     fBytesReceived= 0;
  2202.     fConnectTime= 0;
  2203.  
  2204.     // If this is gopher+ server, then it will send first line as one of these
  2205.     //    which we must eat here...
  2206.     //    +12345<crlf>        number of bytes of data
  2207.     //    +-1<crlf>                data to dot-cr-lf
  2208.     //    +-2<crlf>                data to close-of-connection
  2209.  
  2210.     //if (fIsPlus != kGopherPlusNo) // << this is bad -- do w/o and trust checks of +##, --1 to work
  2211.     if (fProtocol == kGopherprot) 
  2212.     { 
  2213.         char* aLine= fTalker->RecvLine();
  2214.         if (aLine) {
  2215.         
  2216.             if (*aLine == '+' && (strlen(aLine) < 20 || fIsPlus == kGopherPlusYes)) {
  2217.                 fBytesExpected = atol( aLine+1);
  2218.                 DeleteInfo(); // zero it...
  2219.                     // bug in kTCPStopAtdotcrlf, switch it to kTCPStopAtclose for now...
  2220.                 if (fBytesExpected == kReadToDotCRLF) fBytesExpected= kReadToClose;
  2221.                 else if (fBytesExpected > 0) fTransferType= kTransferBinary; //?? always?
  2222.                 MemFree( aLine);
  2223.                 }
  2224.                 
  2225.             else if ( StrStr( aLine,"--1") == aLine) {
  2226.                         ////  error message:
  2227.                         //--1
  2228.                         //1 Don Gilbert <Archive@Bio.Indiana.Edu>
  2229.                         //0- Cannot access that directory
  2230.                         //  1== Item is not available, 2 == Try again later, 3 == Item has moved...
  2231.                 char* serror = (char*)gEmptyString;
  2232.                 char* smail= fTalker->RecvLine();
  2233.                 if (smail) {
  2234.                     long len= strlen(smail);
  2235.                     if (smail[len] == kCR) smail[len]= 0;
  2236.                     serror= fTalker->RecvLine();
  2237.                     if (serror) {
  2238.                         len= strlen(serror);
  2239.                         if (serror[len] == kCR) serror[len]= 0;
  2240.                         }
  2241.                     }
  2242.                 //CloseQuery();
  2243.                 DeleteInfo(); // zero it...
  2244.                 Nlm_Message (MSG_ERROR, "Gopher server error: %s %s", smail, serror);
  2245.                 fBytesExpected= 0;
  2246.                 fThreadState= kThreadDoneReading; // need an error state ??
  2247.                 fTalker->SetEndOfMessage(true);
  2248.                 MemFree( aLine);
  2249.                 return;
  2250.                 }
  2251.                 
  2252.             else {
  2253.                 //long linelen= StrLen(aLine);
  2254.                 //MemExtendCat( &fInfo, fInfoSize, aLine, linelen+1);
  2255.                 MemFree( fInfo);
  2256.                     // patch for binary data w/ nulls
  2257.                 fInfoSize= fTalker->NewBytesReceived();
  2258.                 fInfo= aLine; // aLine is made new in RecvLine()
  2259.                 if ( MemChr(aLine, 0, fInfoSize-1) != NULL)  
  2260.                     fTransferType= kTransferBinary; //kTransferText;
  2261.                 }
  2262.             }
  2263.         }
  2264. }
  2265.  
  2266.  
  2267. void DGopher::CloseQuery()
  2268. {
  2269.     if (fIsLocal) {
  2270.         if (fReplyFile) delete fReplyFile;
  2271.         fReplyFile= NULL;
  2272.         }
  2273.     else if (fTalker) {
  2274.         fBytesReceived= fTalker->TotalBytesReceived();
  2275.         fConnectTime= fTalker->ConnectTime();
  2276.         delete fTalker; 
  2277.         }
  2278.     ClearAskReply();  
  2279.     fTalker= NULL; 
  2280.     fThreadState= kThreadDoneReading;
  2281. }
  2282.  
  2283.  
  2284. void DGopher::ReadAChunk( long maxchunk)
  2285. {
  2286.   //fThreadState= kThreadStarted;
  2287.     if (fIsLocal && fReplyFile) {
  2288.         char* data;
  2289.         ulong datalen;
  2290.     
  2291.         if (maxchunk <= 0 || maxchunk > 32000) maxchunk = 32000;
  2292.         data = (char*)MemNew( maxchunk+1);
  2293.         datalen= MIN( fBytesExpected - fBytesReceived, maxchunk);
  2294.         fReplyFile->ReadData(data, datalen);
  2295.         if (datalen>0) {
  2296.             data[datalen]= 0;
  2297.             if (fInfo == NULL || fInfoSize == 0) {
  2298.                 if (fInfo) MemFree( fInfo);
  2299.                 fInfo= data; data= NULL; //fInfo= (char*) MemDup(data, datalen+1);
  2300.                 fInfoSize= datalen;
  2301.                 }
  2302.             else {
  2303.                 MemExtendCat( &fInfo, fInfoSize, data, datalen+1); // +1 for 0
  2304.                 fInfoSize += datalen;
  2305.                 }
  2306.             fBytesReceived += datalen;
  2307.             }
  2308.         MemFree( data);
  2309.         if (fReplyFile->EndOfFile() || datalen<maxchunk)     
  2310.             fThreadState= kThreadDoneReading;
  2311.         }
  2312.     else if (fTalker) {
  2313.         fInfo= fTalker->ReadWithChecks(fInfoSize, fBytesExpected, (fTransferType == kTransferText), 
  2314.                                         maxchunk, fInfo);
  2315.         }
  2316.     //fThreadState= kThreadDataOld;
  2317. }
  2318.  
  2319.  
  2320. void DGopher::ReadTask() 
  2321. {    
  2322.     fThreadState= kThreadNotStarted; // ! need to reset this somewhere for used gophers
  2323.     if (fHasAsk && fHaveReply) {  // && fThreadState == kThreadNotStarted
  2324.         fQueryPlus= fReplyData;  
  2325.         }
  2326.     OpenQuery();
  2327.     if (fThreadState == kThreadDoneReading) return;
  2328.     DTask* readTask= newTask(cRead, kGopherTask);
  2329.     readTask->SetRepeat(true);
  2330.     PostTask(readTask);
  2331. }
  2332.  
  2333. void DGopher::InfoTask() 
  2334. {    
  2335.     fThreadState= kThreadNotStarted; // ! need to reset this somewhere for used gophers
  2336.     fQueryPlus= "\t!"; 
  2337.     DGopher::OpenQuery(); // bypass class overrides of OpenQuery, which set fQueryPlus !!
  2338.     if (fThreadState == kThreadDoneReading) return;
  2339.     DTask* aTask= newTask(cInfo, kGopherTask);
  2340.     aTask->SetRepeat(true);
  2341.     PostTask(aTask);
  2342. }
  2343.  
  2344. Boolean DGopher::IsMyTask(DTask* theTask) 
  2345. {
  2346.     if (theTask->fKind == kGopherTask) {
  2347.         ProcessTask( theTask);
  2348.         return true;
  2349.         }
  2350.     else 
  2351.         return DTaskMaster::IsMyTask(theTask);
  2352. }
  2353.  
  2354. void DGopher::ProcessTask(DTask* theTask) 
  2355. {
  2356.     Boolean more;
  2357.     if (theTask->fNumber == cRead) {
  2358.         if (fIsLocal)
  2359.             more= (fReplyFile && !fReplyFile->EndOfFile() 
  2360.                         && (fBytesExpected - fBytesReceived)>0);
  2361.         else
  2362.             more= (fTalker && !fTalker->EndOfMessage());
  2363.         if (more)
  2364.             ReadAChunk(kReadMaxbuf1);
  2365.         else {
  2366.             theTask->SetRepeat(false);
  2367.             FinishRead();
  2368.             CloseQuery();
  2369.             }
  2370.         }
  2371.         
  2372.     else if (theTask->fNumber == cInfo) {
  2373.         if (fIsLocal)
  2374.             more= false;
  2375.         else
  2376.             more= (fTalker && !fTalker->EndOfMessage());
  2377.         if (more) 
  2378.             ReadAChunk(kReadMaxbuf1);
  2379.         else {
  2380.             theTask->SetRepeat(false);
  2381.             FinishRead();
  2382.             CloseQuery();
  2383.             
  2384.             if (fInfo && fInfoSize > 0) {
  2385.                 if (*fInfo == '+') fIsPlus = kGopherPlusYes;
  2386.                 else fIsPlus= kGopherPlusNo;
  2387.                 }
  2388.             SetPlusInfo( fIsPlus, fHasAsk, fInfo); // is fInfo safe for use? seems okay
  2389.             
  2390.             char *info= (char*) MemGet(1,true);
  2391.             this->ToServerText( info, 0);
  2392.             
  2393.             if (fIsPlus == kGopherPlusYes) {
  2394.                 // tack on all but first +INFO: line
  2395.                 char *cp, *ep;
  2396.                 char endbuf[6];
  2397.                 StrNCpy( endbuf, LineEnd, 4);
  2398.                 StrCat( endbuf, "#");
  2399.                 cp = StrChr(fInfo,*LineEnd);
  2400.                 while (cp) {
  2401.                     cp++;
  2402.                     ep= StrChr( cp, *LineEnd);
  2403.                     if (ep) *ep= 0;
  2404.                     StrExtendCat( &info, endbuf);
  2405.                     StrExtendCat( &info, cp);
  2406.                     cp= ep; if (cp) cp += LineEndSize;
  2407.                     }
  2408.                 StrExtendCat( &info, LineEnd);
  2409.                 }
  2410.                 
  2411.             MemFree( fInfo);
  2412.             fInfo= info;
  2413.             fInfoSize= StrLen(info);
  2414.             fThreadState= kThreadDoneReading;
  2415.             }
  2416.         }
  2417. }
  2418.  
  2419.  
  2420.  
  2421.  
  2422.  
  2423.  
  2424.  
  2425. short DGopher::CommandResult()
  2426. {
  2427.         // ?? mapping of command due to mapping of these values ?? see CGopherMap
  2428.         return fCommand;
  2429. }
  2430.  
  2431.  
  2432. void DGopher::SetAskReply( CharPtr data, DFile* replyFile)
  2433. {
  2434.     if (fHasAsk && (data || replyFile)) {
  2435.         fHaveReply= true;
  2436.         fReplyData= data;
  2437.         fReplyFile= replyFile;
  2438.         }
  2439. }
  2440.  
  2441. void DGopher::ClearAskReply()
  2442. {
  2443.     if (fReplyData) {
  2444.         fReplyData= (char*) MemFree( fReplyData);
  2445.         }
  2446.     //fReplyFile= (DFile*) FreeIfObject( fReplyFile); !!NO, leave this to creator of DFile !
  2447.     fReplyFile= NULL;
  2448.     fHaveReply= false;
  2449. }
  2450.  
  2451.  
  2452.  
  2453. short DGopher::ThreadProgress(short /*update*/)
  2454. {
  2455.     if (fTalker) {
  2456.         fBytesReceived= fTalker->TotalBytesReceived();
  2457.         fConnectTime= fTalker->ConnectTime();
  2458.         }
  2459.     return fThreadState;
  2460. }
  2461.  
  2462.  
  2463. short DGopher::ItemForm(short viewchoice)
  2464. {
  2465.             // we don't want to repeat mapping here and in DoIt() !!
  2466.     if (fViews && (fViews->GetSize()>0) && gGopherMap) {
  2467.         if (viewchoice<0) fViewchoice= 0; else fViewchoice= viewchoice;
  2468.         DGopherMap* mapper= gGopherMap->MatchHandlerKind( this);
  2469.         if (mapper) mapper->Map(this);
  2470.         }
  2471.  
  2472.     fViewchoice= viewchoice;
  2473.     if (viewchoice == kGetItemInfo) 
  2474.         return cGopherGetInfo;
  2475.     else if (fHasAsk) {
  2476.         if (fHaveReply)  
  2477.             return this->CommandResult();
  2478.         else  
  2479.             return cNewGopherAsk;
  2480.         }
  2481.     else
  2482.         return this->CommandResult();        
  2483. }
  2484.  
  2485.  
  2486. //Local ??
  2487. inline void GoDrawLine( Nlm_RecT& area, char* str)
  2488. {
  2489.     if (str) {
  2490.         Nlm_DrawString(&area, str, 'l', false);
  2491. #ifndef WIN_MAC
  2492.             // XWin/MWin DrawString doesn't set NlmPoint (GetPoint fails to move)
  2493.         Nlm_MoveTo( area.left + Nlm_StringWidth(str), area.top);
  2494. #endif
  2495.         }
  2496. }
  2497.  
  2498. void DGopher::DrawTitle( Nlm_RecT& area)  
  2499. {
  2500.     ::GoDrawLine( area, fLink + fItitle);
  2501. }
  2502.  
  2503. short DGopher::WidthTitle()  
  2504. {
  2505.     if (fLink) return Nlm_StringWidth( fLink + fItitle);
  2506.     else return 0;
  2507. }
  2508.  
  2509.  
  2510. void DGopher::DrawAbstract( Nlm_RecT& area)  
  2511. {
  2512.     if (fAbstract) fAbstract->Draw( area);
  2513. }
  2514.  
  2515. short DGopher::HeightAbstract()  
  2516. {
  2517.     if (fAbstract) return fAbstract->Height();
  2518.     else return 0;
  2519. }
  2520.  
  2521.  
  2522. void DGopher::DrawAdmin( Nlm_RecT& area)  
  2523. {
  2524.     ::GoDrawLine( area, fAdminEmail);
  2525. }
  2526.  
  2527. short DGopher::WidthAdmin()  
  2528. {
  2529.     if (fAdminEmail) return Nlm_StringWidth( fAdminEmail);
  2530.     else return 0;
  2531. }
  2532.  
  2533.  
  2534. void DGopher::DrawPlus( Nlm_RecT& area)  
  2535. {
  2536.     char* str= NULL;
  2537.     if (fIsMap) str= "#";
  2538.     else if (fHasAsk) str= "?";
  2539.     else if (fIsPlus >= kGopherPlusYes) str= "+";
  2540.     ::GoDrawLine( area, str);
  2541. }
  2542.  
  2543. short DGopher::WidthPlus()  
  2544. {
  2545.  return Nlm_StringWidth("?");
  2546. }
  2547.  
  2548.  
  2549. void DGopher::DrawKind( Nlm_RecT&  area)  
  2550. {
  2551.     ::GoDrawLine( area, (char*) GetKindName());
  2552. }
  2553.  
  2554. short DGopher::WidthKind()  
  2555. {
  2556.     return Nlm_StringWidth( (char*)GetKindName());
  2557. }
  2558.  
  2559.  
  2560. void DGopher::DrawDate( Nlm_RecT& area)  
  2561. {
  2562.     char* str= NULL;
  2563.     if (fDate) str= fDate;
  2564.     else if (fIdate) str= fLink + fIdate;
  2565.     ::GoDrawLine( area, str);
  2566. }
  2567.  
  2568. short DGopher::WidthDate()  
  2569. {
  2570.     if (fDate) return Nlm_StringWidth( fDate);
  2571.     else if (fIdate) return Nlm_StringWidth( fLink + fIdate);
  2572.     else return 0;
  2573. }
  2574.  
  2575.  
  2576. void DGopher::DrawSize( Nlm_RecT& area)  
  2577. {
  2578.     char* str= NULL;
  2579.     if (fDataSize) ::GoDrawLine( area, fDataSize);
  2580.     else if (fIsize) ::GoDrawLine( area, fLink + fIsize);
  2581. }
  2582.  
  2583. short DGopher::WidthSize()  
  2584. {
  2585.     if (fDataSize) return Nlm_StringWidth( fDataSize);
  2586.     else if (fIsize) return Nlm_StringWidth( fLink + fIsize);
  2587.     else return 0;
  2588. }
  2589.  
  2590. void DGopher::DrawPath( Nlm_RecT& area)  
  2591. {
  2592.  if (fIpath) ::GoDrawLine( area, fLink + fIpath);
  2593. }
  2594.  
  2595. short DGopher::WidthPath()  
  2596. {
  2597.     if (fIpath) return Nlm_StringWidth( fLink + fIpath);
  2598.     else return 0;
  2599. }
  2600.  
  2601.  
  2602. void DGopher::DrawHost( Nlm_RecT& area)  
  2603. {
  2604.     if (fIhost) ::GoDrawLine( area, fLink + fIhost);
  2605. }
  2606.  
  2607. short DGopher::WidthHost()  
  2608. {
  2609.     if (fIhost) return Nlm_StringWidth( fLink + fIhost);
  2610.     else return 0;
  2611. }
  2612.  
  2613. void DGopher::DrawPort( Nlm_RecT& area)  
  2614. {
  2615.     char snum[256];
  2616.     Dgg_LongToStr( fPort, snum, 0, 255);
  2617.     ::GoDrawLine( area, snum);
  2618. }
  2619.  
  2620. short DGopher::WidthPort()  
  2621. {
  2622.     char snum[256];
  2623.     Dgg_LongToStr( fPort, snum, 0, 255);
  2624.     return Nlm_StringWidth(snum);
  2625. }
  2626.  
  2627. void DGopher::DrawURL( Nlm_RecT& area)  
  2628. {
  2629.     ::GoDrawLine( area, (char*)GetURL());
  2630. }
  2631.  
  2632.  
  2633. void DGopher::DrawIconSub( Nlm_RecT& area) 
  2634. {
  2635.     short idplus;
  2636.     DIcon* ico= NULL;
  2637.     switch (gIconSize) {
  2638.         case 0 : idplus= 20000; break;
  2639.         case 1 : idplus= 10000; break;
  2640.         case 2 : 
  2641.         default: idplus= 0; break;
  2642.         }
  2643.         
  2644.     if (gGopherIcons) ico= gGopherIcons->IconById( gGopherIconID + idplus); 
  2645.     Nlm_InsetRect( &area,1,1); //? do we really want this?
  2646.     if (ico) ico->Draw(area);
  2647. }
  2648.  
  2649. void DGopher::DrawIcon( Nlm_RecT& area, short size)  
  2650. {
  2651.     gGopherIconID= kDefaultIcon;
  2652.     gIconSize= size;
  2653.     DrawIconSub( area);
  2654. }
  2655.  
  2656. short DGopher::WidthIcon(short size)  
  2657. {
  2658.     switch (size) {
  2659.         case 0 : return 16;  //12 + 4
  2660.         case 1 : return 20;  //16 + 4
  2661.         case 2 : return 36; 
  2662.         default: return 36; 
  2663.         }
  2664. }
  2665.  
  2666.  
  2667.  
  2668.  
  2669.  
  2670.  
  2671. struct goTypeRec {
  2672.     char kind;
  2673.     const char* name;
  2674.     Boolean supported:1;
  2675.     };
  2676.  
  2677. Local goTypeRec gGopherTypes[] = {
  2678.     {kTypeFile, "Document", true},
  2679.     {kTypeFolder, "Folder", true},
  2680.     {kTypeQuery, "Query", true},
  2681.     {kTypeBinary, "Binary file", true},
  2682.     {kTypeImage, "Image", true},
  2683.     {kTypeSound, "Sound", true},
  2684.     {kTypeMovie, "Movie", true},
  2685.     {kTypeNote, "Note", true},
  2686.     {kTypeHtml, "HTML document", true},
  2687.     {kMailType, "Mailto", true},
  2688.     {kTypeTelnet, "Telnet link", false},
  2689.     {kTypeTn3270, "TN3270 link", false},
  2690. #if 0    
  2691.     {kTypeCSO, "CSO Phonebook", false},
  2692.     {kTypeWhois, "Whois Phonebook", false},
  2693.     {kTypeBinhex, "Binhex file", false},
  2694.     {kTypeUuencode, "Uuencoded file", false},
  2695. #endif
  2696.     {kTypeError, "Unknown type", true},
  2697.     {0,0} 
  2698.     };
  2699.  
  2700.  
  2701. //static
  2702. const char* DGopher::GetTypeName( short listitem)
  2703. {
  2704.     if (listitem>=0 && listitem < (sizeof(gGopherTypes) / sizeof(goTypeRec)))
  2705.         return gGopherTypes[listitem].name;
  2706.     else
  2707.         return NULL;
  2708. }
  2709.  
  2710. char DGopher::GetTypeVal( short listitem)
  2711. {
  2712.     if (listitem>=0 && listitem < (sizeof(gGopherTypes) / sizeof(goTypeRec)))
  2713.         return gGopherTypes[listitem].kind;
  2714.     else
  2715.         return 0;
  2716. }
  2717.  
  2718. Boolean DGopher::GetTypeSupport( short listitem)
  2719. {
  2720.     if (listitem>=0 && listitem < (sizeof(gGopherTypes) / sizeof(goTypeRec)))
  2721.         return gGopherTypes[listitem].supported;
  2722.     else
  2723.         return 0;
  2724. }
  2725.  
  2726. short DGopher::GetTypeItem( char theType)
  2727. {
  2728.     short i;
  2729.     for (i=0; i < (sizeof(gGopherTypes) / sizeof(goTypeRec)); i++) {
  2730.         if (gGopherTypes[i].kind == theType) return i;
  2731.       }
  2732.     return -1;
  2733. }
  2734.  
  2735.  
  2736.  
  2737. Local goTypeRec gProtoTypes[] = {
  2738.     { DGopher::kGopherprot, "gopher://", true },
  2739.     { DGopher::kHTTPprot, "http://", true },
  2740.     { DGopher::kFileprot, "file://", true },
  2741.     { DGopher::kFTPprot, "ftp://", true },
  2742.     { DGopher::kSMTPprot,"mailto:", true },
  2743.     { DGopher::kWAISprot, "wais://", false },
  2744.     { DGopher::kTelnetprot, "telnet://", false },
  2745.     { DGopher::kTN3270prot, "tn3270://", false },
  2746.     { DGopher::kNNTPprot, "news:", false },
  2747.     { DGopher::kFingerprot, "finger:", true },
  2748.     { DGopher::kWhoisprot, "whois:", true },
  2749.     { DGopher::kUnsupportedProt, "unsupported://", true },
  2750.     { DGopher::kUnknownProt, "unknown://", true },
  2751.     {0,0}
  2752.     };
  2753.  
  2754. //static
  2755. const char* DGopher::GetProtoName( short listitem)
  2756. {
  2757.     if (listitem>=0 && listitem < (sizeof(gProtoTypes) / sizeof(goTypeRec)))
  2758.         return gProtoTypes[listitem].name;
  2759.     else
  2760.         return NULL;
  2761. }
  2762.  
  2763. char DGopher::GetProtoVal( short listitem)
  2764. {
  2765.     if (listitem>=0 && listitem < (sizeof(gProtoTypes) / sizeof(goTypeRec)))
  2766.         return gProtoTypes[listitem].kind;
  2767.     else
  2768.         return 0;
  2769. }
  2770.  
  2771. Boolean DGopher::GetProtoSupport( short listitem)
  2772. {
  2773.     if (listitem>=0 && listitem < (sizeof(gProtoTypes) / sizeof(goTypeRec)))
  2774.         return gProtoTypes[listitem].supported;
  2775.     else
  2776.         return 0;
  2777. }
  2778.  
  2779. short DGopher::GetProtoItem( char theProto)
  2780. {
  2781.     short i;
  2782.     for (i=0; i < ( sizeof(gProtoTypes) / sizeof(goTypeRec)); i++) {
  2783.         if (gProtoTypes[i].kind == theProto) return i;
  2784.       }
  2785.     return -1;
  2786. }
  2787.  
  2788.  
  2789.  
  2790.  
  2791.